Trying something new – building the Life in the Dark interactive splash | Digital Media at the NHM

As part of the museum’s Life in the Dark exhibition digital content offer, we decided to try something new for our exhibition landing page to help promote interest and engagement.  We ended up using a new mix of front end technologies to build it, so here’s a (long) technical walk through of the various challenges and solutions we encountered.

The concept was to show a life in the dark visual scene, where specimens showcased in the exhibition could appear based on a user interaction.

It was designed to be an optional engagement and promotional feature, the interactive element would be limited to a splash component on the existing exhibition page, replacing the existing banner image but not impacting the rest of the promotional page content and book tickets CTA.

As our target market was primarily mobile, the limited screen real-estate available within the existing page layout pushed us into making an element that could scroll independently of the rest of the page.


Florence, our UX designer spent some time researching and liaising with the exhibition designers to ensure what we were producing fit within the style and content of the physical exhibition.  The concept involved featuring several environments (forest, cave, deep sea) where relevant creatures could be discovered.

We went through several different iterations, user testing at key stages and then evaluating what worked and what didn’t with the user interface.


Our AEM content management system is based on a collection of predefined components that editors can use to construct and publish pages, whilst we do have some interactive components (carousels, popups etc.) these are JQuery driven and mostly functional in utility.

Our development stack is transitioning to a more front-end technology focus in order to meet an increasing requirement for richer and more flexible user experiences, so I felt it was important to trial a new way we could incorporate technologies such as React and Webpack within our existing platform.  It was certainly not strictly necessary and could have just used JQuery as before, but there were useful performance and code delivery advantages that arose even from this simple use case that proved successful.


React is a Javascript framework that lets developers create hierarchical components that can be used to modularise the appearance and behaviour of webpage UI elements.  It’s popularity stems from the fact that It’s less strict in terms of application architecture compared to other more opinionated frameworks (Angular, Vue), coupled with the performance benefits of its virtual DOM.


Webpack has become the defacto Javascript delivery framework, it can customised in many ways to increase the efficiency of bundling and delivering code to the browser.  As web pages become richer in order to meet business and audience demands,  there are far more assets that need to be downloaded to the browser.  To insure the first page load and interaction remain fast enough so users aren’t put off,  Webpack offers code-splitting that can be customised by the developer so that a good compromise between initial page load and UI richness can be reached.


SVG files are image data in an XML document format broken down into collections of shapes, lines, groups and fills which make up the image.  This makes the image resolution independent which is one of the core benefits of SVG.

There are several ways to include SVG in a web page, they can be loaded into an <img>, <object> or <iframe> element, or they can be loaded inline, where the entire SVG XML structure becomes part of the page’s mark up.  The latter 3 approaches allow the use of Javascript to manipulate the SVG structure, so individual SVG elements can be clicked, animated or modified, much like standard HTML elements (though with some differences).  In addition there’s also the ability to modify the size and aspect ratio of the SVG element, allowing the developer to zoom in and pan etc. within the SVG graphic. This is dictated by the SVG’s scaling behaviour attributes – height/width, viewbox and preserveAspectRatio.

<svg id=”Layer_1″ viewBox=”5 5 1914 5210″ height=”100%” preserveAspectRatio=”xMidYMin meet”>

A simplified explanation is that the viewBox defines the size and shape of the window into the SVG content, the preserveAspectRatioattribute controls the individual cropping behaviour for the horizontal and vertical.

A good resource that explains how this works and differs from normal HTML image scaling is

All this meant that the different screen sizes (mobile/tablet/desktop+ landscape/portrait) we needed to cater for could have optimised display and behaviour all from the same SVG file.

Florence worked on the SVG assets in Adobe Illustrator.  Since we were treating the animals as separate elements it made sense to keep the main scene background and individual creatures as separate SVG files.  SVG content can be described by a mix of complex PATHs described by many coordinates, or more simple defined shapes (rectangles, circles, ellipses etc.).  In addition these defined elements can be filled with colour or gradient definitions (similar to CSS) and also grouped into larger elements so that they can have common styling definitions and behaviour.

A large part of creating SVG for web is ensuring these generated definitions are as efficient and optimised as possible, removing excessive PATHs or duplicates is vital.  Ina similar way developers need to reduce the number of HTML elements in a page to increase both page load, interaction and rendering performance, the SVG document needs to be as light as possible.  The real power of SVG is as programmable graphic asset, which can be modified by data or user interaction to create many different types of digital interactive elements. 


For our simple web app, we ended up with 4 React components.

  • Main container
  • the background SVG
  • the ‘hidden’ layer component
  • animal SVG component

The container would perform the initialisation, setting the starting state for all the child components as well as implementation specific calculations (e.g. mobile or desktop browser, Firefox needs different viewbox values compared to other browsers to render the same SVG view and certain CSS values were calculated and injected only once the size of the screen was known).

Each SVG component loaded it’s SVG asset inline directly into an HTML <div> element, which meant the entire SVG content was included in the compiled output.  This reduces the number of http requests which greatly benefits initial page load speed.

Each React component implements it’s own listeners and state, with global events (like the zoom-in animation performed by the container component when a specific animal SVG is clicked) triggered up through the components via functions passed as properties.


Since the interactive was meant to show creatures contextually within their respective environment (we didn’t want to see the angler fish in the forest when a user resizes their browser or flips their mobile round) , it was vital that the creatures could be positioned absolutely within their parent container (which contains the background SVG), but that content would work responsively across all devices.

We had 3 layers in the background SVG (forest, cave, deep sea  – all varying heights ) so my approach was to create a hidden container for each in which the individual creatures could be absolutely positioned regardless of screen size, leaving the layers to handle responsive scaling.

The hidden layers were coloured during development to help with manual tweaking of the absolute CSS positioning of the creature elements.

I used Sass for the CSS build, breaking up the styling behaviour roughly into 3 partials:

  • Main layout including responsive media queries
  • Animal absolute positioning
  • CSS3 Keyframe animations


One decision we had to make early on was the relative size of the animals in respect to the background image.  We had both small and large, and they needed to be presented at a reasonable scale.  For example to be able to see the moth in sufficient detail it would need to be roughly 2 trees wide on mobile!

To overcome this I ended up with 2 solutions, firstly the mobile CSS media queries would define proportionally larger animal dimensions (e.g. 20% screen width instead of 10% for desktop sizes), secondly, because the tree layer had the most obvious size perspective (trees give a real sense ofscale,  more so than caves andunderwater),  I implemented a ‘zoom’ effect to the clicked animal (Moth, AyeAye) by using the viewBox variable on the background SVG. There was also a delay added so that the animal would only appear at the end of the zoom animation.

I used the popular Greensock animation library to perform the SVG viewbox animations, Greensock lets you define and control changes overtime to any variable available in javascript and CSS, so whilst CSS animations are restricted to existing CSS attributes, Greensock can apply complex sequences to things like SVG values or any variable the developer has defined.

Another animation we added to the forest layer is the background colour change to simulate the sun setting as the view is scrolled down, I was inspired by Florence’s wonderful colours in her graphic and noticed that the gradient affect generated by Illustrator was a simple collection of gradient values:

<g id=”dusk_x5F_bckground”>
      <linearGradient id=”SVGID_1_” gradientUnits=”userSpaceOnUse” x1=”0″ y1=”4656″ x2=”1919″ y2=”4656″ gradientTransform=”matrix(1 0 0 -1 0 5216)”>
      <stop  offset=”6.890849e-02″ style=”stop-color:#292E40″/>
      <stop  offset=”0.1877″ style=”stop-color:#312E47;stop-opacity:0.9773″/>
      <stop  offset=”0.3827″ style=”stop-color:#3B2F4E;stop-opacity:0.94″/>
      <stop  offset=”0.5032″ style=”stop-color:#423754;stop-opacity:0.8557″/>
      <stop  offset=”0.6931″ style=”stop-color:#544E65;stop-opacity:0.7228″/>
      <stop  offset=”0.8399″ style=”stop-color:#666475;stop-opacity:0.62″/>
      <stop  offset=”0.9098″ style=”stop-color:#4F5E6A;stop-opacity:0.3495″/>
      <stop  offset=”1″ style=”stop-color:#2B5459;stop-opacity:0″/>

Greensock lets you bind animations to event listeners, so the browser’s scroll position listener could directly control the SVG gradient values (specifically the y2 attribute above), generating the gradual change of colour.  Scroll listeners historically have a reputation of impacting performance in Javascript, but modern browsers have reduced this, I still implemented a throttling element as a compromise to insure the SVG value’s update rate was not too excessive so it didn’t impact scroll responsiveness on slower devices.

We also wanted to give an underwater feel to the largely black deep sea layer, I’d found several simple animated bubble animations on community code sharing resources like

A lot of these are built in HTML Canvas.  Canvas is a drawing API built into browsers that gets interpreted directly by a devices’ graphics layer, which makes it very performant but also very different to work with compared to HTML or SVG elements.

The bubbles I went with in the end are generated by a javascript class which creates several instances of bubbles (really colored circles) with random speed, size, turbulence and starting position attributes, these are then animated upwards to the top of the canvas element and re-added to the bottom so that the animation loops continuously.  This new HTML canvas element simply needed to be layered on top of the background SVG but below the animal SVGs using CSS z-index.

Finally the animal animations were done entirely with CSS, they appear hidden by default and fade in when clicked.  Each animal SVG component has it’s own additional element that used a CSS box-shadow for the glow effect, using keyframe animations for the blinking fade-in/fade-out effect.  Additionally some of the animal components had random looping motion via CSS transform animations.


With the target platform being mobile, I was constantly keeping track of the total page payload of the interactive, it was vital it wasn’t excessively larger than the existing splash image it was replacing.  Detail in the SVG’s were really pushing the total size so Florence had to rework the SVG’s several times to reduce the size by removing detail, there are several tools to minify SVG but it still requires both designer and developer to work close together for optimal results.

The total payload would also include the ReactJS and code dependencies, the compiled code and all the inline SVG’s (detailed background and 5 animals), the total was 1.3MB after minification, way larger than the target 500KB I was hoping for.

Webpack is a great tool to fine tune how code and assets are delivered on demand,  so since I knew the initial state of the scrollable interactive would only need to show the 2 forest creatures and that the actual SVG would only need to be available when a user interacts with it, I came up with solution to delay loading of the assets until necessary.

Webpack lets you break up a webapp into bundled chunks, which would be downloaded when required in code.  I set up all the animal SVG’s as these individual chunks and added the React waypoint library to detect when a specific layer is in view.  The waypoint logic would trigger the download of the required assets, so as the user scrolls down, the SVG’s are loaded in, this worked well since the SVG’s only had to be loaded and not visible, I could delay the glow animation and interactivity until each asset was ready. This meant the initial payload was now around 600KB (the core code, background SVG and 2 animal SVG’s), ensuring the page load speed wasn’t too negatively impacted.

We had to ensure that the solution would be acceptable for all browsers we support, as we were using fairly modern SVG features and ES6 Javascript (which modern browsers support), I used Babel to transpile the javascript code to ES5 so that older browsers could run it.  During initialization, specific SVG functionality was also tested, if it failed (i.e. old Internet Explorer browsers), the React app would fall back to displaying the original Splash JPG image instead.


As the React app was developed outside of the AEM CMS, I had to come up with a way to deploy it into the AEM codebase as a single purpose AEM component that could be just dropped into an existing user edited AEM page to replace the original splash image.  AEM breaks up UI components into templates and client libraries which need to be setup in a specific configuration.

Our template only needed to be a target <div> to serve as the entry point of the React rendered app, with the bundled CSS and JS chunks outputted by the webpack build copied into the correct locations that AEM requires to serve as component assets.


As we were trialing something new on a high impact page, we decided to A/B test.  We’d previously used Google Optimize to create A/B variants. However since this performs on the fly dynamic replacement of HTML content, I felt this introduced too many unknowns with the webpack code delivery solution, so we ended up creating 2 distinct AEM pages and used Google Redirect test instead to split traffic between the variants.

We were primarily testing to see if the separate book tickets call to action button would be negatively or positively impacted by the new interactive element on the page, it turned out there was no significant difference.

Since the interactive was not designed in a targeted way to prompt a commercial response, this probably isn’t too surprising.  The analytics do point to a good level of engagement however, with around 20% of page visitors scrolling down and clicking on the last creature. 

The project also served as a great introduction into how modern tooling and browser capabilities (specifically webpack and SVG in code) can allow for richer and more optimized UI experiences, vital as we explore different ways to engage and inspire our digital audiences.