Burning in Hell…o, World.


Lighting my first black handle and worrying my Mom. On the web.

I grew up in a religous community in south Louisiana and my extremely devout (and awesome) mother raised us Catholic. While I haven’t practiced since making my confirmation quite some time ago, the catholic guilt still shows up to effect my decision making occasionally. Oddly enough, this hasn’t really come up when choosing clients within my rock and roll career until I recently worked for a band called Ghost.

Ghost is a doom rock band from Sweden (where else.) The lead singer, Papa Emeritus, dresses like an evil pope and fronts a hooded group of musicians refered to as “Nameless Ghouls.” All of their identities are kept secret. For real.

While your initial impression of this act might lead you to believe they sing black metal, the first record Opus Eponymous, is shockingly accessible rock. Perhaps that’s exactly what makes this band so nefarious. The ease of accessiblity they apply to their highly satanic lyrics is pure evil, and you’ll find yourself singing along in no time.

Needless to say, I had a decision to make. I didn’t want to let my Mom down, but at the end of the day it’s all art and the opportunity to do something cool for Ghost was really interesting. And you know who else works with them? Dave Grohl. The nicest guy in rock.

Surely he cancels out some of that evil…

A Haze of Goals #

Ghost were preparing to premiere “Secular Haze,” the first piece of new music since their debut album, live during a special performance in their hometown of Linköping. What also made this particular show unique was the fact that there was going to be a succession of the pope, culminating in a moment where Papa I bids farewell and Papa II appears on stage to sing the new track. Seriously. I can’t make this shit up.

I was commissioned to tease the fact that this was going to occur by slowly releasing pieces of the studio version of “Secular Haze,” and counting down to the “Infestissumam” album announcement, while also building up the band’s first mailing list.

My only guideline was a photo Papa took at an antique store which included a clock surrounded by black candles. According to Papa, the track itself also contained a metronome click track which he’d like me to sync to the ticking of the clock.

Oh, it’s you again, countdown clock. Fine, let’s do this shit.

Here’s what I proposed:

What if we broke the track up into six stems (keys, drums, bass, guitar 1, guitar 2, vocals) and released them one at a time in the form of a lit black candle over the period of a week. Hovering each of these candles would also isolate the associated stem, allowing fans to experience each instrument individually. These candles would surround a backwards ticking clock which synced to the song’s click track. In addition, the page would include a roman numerical countdown relating to the actual album announcement and an email “offering box.”

Papa agreed and I got to work.

Black Candles & Stems #

Secular Haze

I started by created the candles as HTML elements so I could reference them as an array using jQuery later. Each was given an HTML5 data attribute of type to distinguish which instrument they related to.

  .candle{data: {type: 'KEYS'}}
  .candle{data: {type: 'GTR1'}}
  .candle{data: {type: 'DRUMS'}}
  .candle{data: {type: 'BASS'}}
  .candle{data: {type: 'GTR2'}}
  .candle{data: {type: 'VOCALS'}}

Initialize Sounds #

Since we’re trying to sync six different stems and a master track, we’ll need to define a few functions that utilize soundManager2 to preload all sounds and play them at the same time.

Let’s begin by initializing the sounds. We have a TICK, TOCK, & MASTER, in addition to each STEM, which we pull in dynamically via the HTML elements. This way I can manually add each stem throughout the campaign simply by adding another HTML element, which is crucial to keeping the rollout obscure. If you add all the pieces at once, fans will figure out what to expect in the future and where the sounds are located.

Trust me.

sounds = [
  id: 'TICK', loaded: 0, stem: false
  id: 'TOCK', loaded: 0, stem: false
  id: 'MASTER', loaded: 0, stem: false

$.each $('#candles').children(), (index, value) ->
  sounds.push id: $(value).data('type'), loaded: 0, stem: true

Preload All Sounds #

Next we’ll define a function which loads each of the sounds and keeps track of the loading progress of each track individually and all of them as a whole. Let’s break down what’s happening in the code comments.

loadAllSounds = (sounds, callback) ->
  totalSounds  = sounds.length
  loadedSounds = 0
  totalLoaded  = 0

  # This function keeps track of the progress of each loading sound
  # and combines all progresses into a totalLoaded variable
  # which we can use to build a page loading UI element

  aSoundLoading = (id, loaded) ->
    s        = _.where(sounds, {id: id})[0]
    s.loaded = loaded

    totalLoaded   = 0
    totalLoaded  += sound.loaded for sound in sounds

  # This function is called each time a sound loads completely
  # If all sounds are loaded, the callback function is invoked

  aSoundLoaded = () ->
    callback() if loadedSounds > totalSounds - 1

  # Loop through each sound and create the sound object
  # using soundManager's createSound function
  # Include the aSoundLoaded() and aSoundLoading() functions

  for sound in sounds
      id: sound.id
      url: "/mp3/#{ sound.id }.mp3"
      onload: ->
      whileloading: ->
        aSoundLoading @id, @bytesLoaded / @bytesTotal

All Sounds Loaded #

When all sounds are loaded, we’ll play the MASTER track and start ticking the clock via a function defined later. In addition, we’ll simultaneously play each STEM on mute. More on that in a bit.

allSoundsLoaded = () ->
  soundManager.play 'MASTER',
    onplay: ->

  for stem in _.where(sounds, { stem: true })

All of these functions above are initialized by a single line which passes the sounds array and allSoundLoaded callback function.

loadAllSounds(sounds, allSoundsLoaded)

Stem Isolation #

As noted earlier, we started playing the stems simultaneously in parallel with the MASTER track to sync their positioning. Now all we need to do is toggle the muting functions of each STEM (and the MASTER) to isolate each sound when the mouse hovers over each candle.

$('.candle').mouseenter (e) ->
  soundManager.unmute $(e.target).data('type')
  soundManager.mute 'MASTER'

$('.candle').mouseleave (e) ->
  soundManager.mute $(e.target).data('type')
  soundManager.unmute 'MASTER'

Blurred Ticking #

I’ve also added an easter egg which lowers the ticking sound to barely audible when the user switches tabs or unfocuses their browser.

$(window).blur ->
  soundManager.setVolume 'TICK', 1
  soundManager.setVolume 'TOCK', 1

$(window).focus ->
  soundManager.setVolume 'TICK', 10
  soundManager.setVolume 'TOCK', 10

Reverse Clock #

Given the BPM of “Secular Haze,” we can create an interval which calculates the current tick position and degree of rotation. This rotation is passed to the CSS3 transform function to rotate the clock hand. In addition, we’ll simply check if the tick is odd or even and alternately play the TICK or TOCK sound.

time = 0
bpm  = 900

tickClock = () ->
  setInterval () ->
    time  += bpm
    tick   = time / bpm
    degree = -(tick * 5.4)
    rotate = "rotate(#{ degree }deg)"

      "transform": rotate

    if playing is false
      if tick%2 is 0
  , bpm

Shadow #

The clock in the original photo Papa sent over included an odd green shadow in the clock face which I photoshopped out initially. I decided it added quite a bit to the aesthetic and retranslated the exact hue as a inset box-shadow which moves with the user’s cursor. The result is subtle and creepy.

$(window).mousemove (e) ->
  halfX    = $(window).width() / 2
  percentX = (halfX - e.pageX) / halfX * 25
  halfY    = $(window).height() / 2
  percentY = (halfY - e.pageY) / halfY * 25

  shadow = "inset #{ percentX }px #{ percentY }px rgba(0,88,38,0.1)"

    "-box-shadow": shadow

Romanized Countdown #

The black candles and clock are accompanied by a roman numeral countdown counting down to the actual album announcement. A quick Google search, led me to a romanize function written by Steven Levithan that converted integers into roman numerals with ease. I converted it to CoffeeScript and dropped it in the code.

romanize = (num) ->
  digits = String(+num).split('')
  key = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"]
  roman  = ""
  i = 3
  while i--
    roman = (key[+digits.pop() + (i * 10)] || "") + roman
  Array(+digits.join("") + 1).join("M") + roman

This function was combined with Edson Hilios’ excellent Final Countdown jQuery library to finish off the countdown.

endTime = new Date("Thu, 20 Dec 2012 14:00:00 GMT-0800")

$('#time').countdown endTime, (event) ->
  if event.type is "hours"
    hours = event.lasting.hours + event.last.days * 24

Spreading the Gospel #

The final aspect of the campaign was quite simple, thanks to Campaign Monitor’s excellent infrastructure. We quickly added an email “offering box,” using their AJAX subscription form that connected to an autoresponder which emailed all new subscribers a free download of “Secular Haze.”

The campaign was concluded when we sent a newsletter to our entire list with the album announcement info.

Oh, and this happened:


You can check out the entire source to see some of the UX logic I left out this writeup. Special thanks to the Shreddit community for making this launch rock and assisting in live bug testing whether I liked it or not. Feel free to send me any questions or comments on Twitter.


Now read this

I ♥ Electronic Mail

Maybe I’ve lost my mind… but I really like designing and developing for email. Yes, email. There’s something about the rigid structure of tables and inline CSS that makes me feel nostalgic, and I like being challenged to do something... Continue →