I apologize that it has been a minute since I've written here. I recently moved back to New Orleans to take a break from Los Angeles and restart my career as a freelancer. It's been a lot of fun getting to make internets with the likes of Rian Rochford, Leda Chang, Jason Feinberg, Harold Gutierrez, and many others again. Today I want to share some details on a Queens of the Stone Age campaign I launched last week in support of their soon to be released record, …Like Clockwork.
If you're in the United States, you can try the experience here before reading this blog. Just type your US number in the blank provided with a 1 in front and click the phone icon to dial.
I met Josh Homme about five years ago while working at SAM. He called me “pubes” because my voice cracked during the meeting, and while we've created a lot of mischief together during Era Vulgaris and Them Crooked Vultures, we haven't worked an entire record cycle together. To say I'm excited is an understatement. Not to mention the new album is a fucking masterpiece.
Josh's visual aesthetic mimics his musical style: dark, mysterious, crass… and he always seems to surround himself with the best talent to make his fucked up visions a reality. This record is no different as he employed the help of Liverpool based illustrator Boneface to create the album artwork and a series of Liam Brazier animated videos set to songs from the record.
Concept
Two weeks ago I jumped on a Skype call with Boneface and Liam to discuss my vision for hyping this series of videos based on an idea I had that actually came from a defunct Tom Petty proposal I wrote recently.
The concept involved allowing the user to feel attached to what they were seeing on screen via a phone call initiated by the subject. So what started as Tom Petty standing in a phone booth, turned into an evil clown mask wearing figure… I love ideas, like Call Drops, which feel like magic but are actually simple builds technically. Fucking with someone's phone is a surefire way to grab their attention and force them into the story you're telling.
Boneface and Liam agreed. So I wrote them a scripted grocery list of assets I needed to pull it off.
OUTRO - Clown hangs up and camera pans out of room.
Within three days, Boneface and Liam had delivered all the assets I needed while also simultaneously working on our animated series. That's some serious hustle. With the assets on hand, it was time for me to get nerdy.
Tech
HTML5 Video
I began development by including each video file within an HTML5 <video> element. All were given the setting of preload to load them automatically (on desktop.) The idle and talk videos were set to loop as they would play continuously until that particular phase was finished.
In order for the HTML5 video to work cross browser and, you'll need to include several formats of each video. Check out Rob Walsh's tutorial for creating these formats using Quicktime and ffmpeg2theora.
Using jQuery, I was able to bind a listener for each videos “ended” event and jump to the next scripted video. For example, when the intro ended, I wanted the idle video to play. So in Coffeescript I wrote:
These were layered and binded in a way to script the entire scene, and also adjust other elements on the page as needed.
I expiremented by creating a sort of video sprite sheet, and setting the play position to each section in a similar manner, but I couldn't get the millisecond precision I needed, causing things to look glitchy. However, I feel like that could technically work if done correctly.
Twilio
User phone numbers were obtained via a simple input form on the page and were asynchronously sent to an action on the server. This function made sure the input was a phone number and then normalized and formatted it accordingly using the excellent Phony gem.
if Phony.plausible?(params["phone_number"])
phone_number = Phony.normalize(params["phone_number"])
phone_number = Phony.formatted(phone_number, format: :international, spaces: '')
end
I then used Twilio to initiate a call, and if successful, create a new user in my local database with the newly obtained phone number.
call = twilio.account.calls.create(
from: ENV['TWILIO_NUMBER'],
to: phone_number,
application_sid: ENV['TWILIO_APP_ID'],
if_machine: 'Hangup'
)
user = User.first_or_create(phone_number: call.to)
Finally, the unique “sid” of the call was sent back to the client application. I'll tell you why in a bit. ;-)
{ sid: call.sid }.to_json
The actual application which is ran for each placed call is part of a seperate action which I specified in my Twilio account. Using Twilio's XML friendly markup, TwiML, I tell the app to Play the clown's dialog and Sms a message once the call is completed. Pretty simple.
response = Twilio::TwiML::Response.new do |r|
r.Play '/phone.wav'
r.Sms 'Return. May 6th. http://likeclockwork.tv', to: phone_number, from: ENV['TWILIO_NUMBER']
end
For US numbers, this only cost us $0.02 per call and we were able to pull geographical data (city, state, zip) about each caller. International was more expensive and some countries, like Brazil, cost as much as $0.33 per call. Next time I would probably stick to US, UK, and those countries that were less than $0.05. Sorry Brazil! You can check Twilio's pricing here.
Pusher
Given the varying times it takes for a phone call to be placed and received, I couldn't show the talking animation for a set time and then transition into the outro. Instead I needed a way to know exactly when a call ended. Luckily Twilio provides a status callback URL, which can notify my app of each completed call. Combine this with a real-time API like Pusher and you've got a 1 to 1 representation of the actual call.
It all starts with that unique “sid” the Twilio call response provides. We'll want to receive that one the client and subscribe to a Pusher event of the same name. When that event is triggered, the hang up animation should be shown.
This works so well that if you hang up on the clown prematurely, he'll react accordingly on screen almost instantly. It would be bad ass if he reacted differently to dropped calls and bad numbers also, but we didn't get that far this time.
Howler.js
Since the app had two audio files, background ambience and dial clicks, I needed a way to play two sounds simultaneously which also worked on mobile. Typically I would use Scott Schiller's excellent SoundManager2 but I didn't think it would handle web audio the way I needed so I found a new library called Howler.js that did exactly what I needed.
And playing the click after each dial is a simple:
click.play();
Looking back, perhaps I could have just added the click audio to each of the dial videos… in fact, maybe I'm an idiot for not doing that… Whatever. It worked.
Outro
If you're in the United States, you can still try the clown dialer here. And be sure to check out “I Appear Missing” the first of our animated videos below:
As always, thanks for reading and checking out the work. You can expect much more open-sourced knowledge here over the summer. Follow me on Twitter for the latest and please let me know if you have any questions or comments.
One proven way to generate extra traffic in Internet marketing is to associate your campaign with major world events somehow. Maybe it's the Superbowl, Oscars, or in the case of my last campaign, the Papal Conclave. The trick is to react quickly and cleverly in an effort to become part of the massive conversation already happening organically.
We wanted Ghost B.C. to have a presence in Austin at SXSW even though they weren't performing. Trying to make an impression among all that noise and BBQ is not easy, but we were handed a, dare I say blessing, when Pope Benedict XVI stepped down and forced an election for a new Pope.
We decided to craft a “Papa Emeritus II for Pope” campaign that existed in both the real and digital world via old school printed flyers and the website papaemeritus.com, which allowed fans to cast their vote for Papa and unlock the band's newest track, Year Zero. The first thing we needed was an image.
Poster
My thinking is that real world campaign “conversion” of a text-heavy flyer design was going to be low within all that noise at SXSW, so I opted to design something much more iconic and dropped in a subtle web URL for good measure. The aesthetic and layout of the poster is an obvious bite of Shepard Fairey's massively successful Hope poster for President Barack Obama's 2008 election campaign with a Ghost B.C. twist, of course.
I began by sketching Papa Emeritus II from a source image with my 24 pack of Sharpies in an effort to abstract his most prominent features like his Mitre, white eye, and ghoulish scowl. This sketch served as a guide I could use in Illustrator to digitally draw all the shapes I needed to create Papa. I found an excellent pattern pack by Grant Friedman that assisted me in creating the cross-hatching which adds a nice lighting variation.
Finally, I dropped the word “POPE” or “PAPA” in as 250pt Helvetica Bold and sent a print ready version of the flyer over to Austin for pasting.
Flyering
On the night of Sunday, March 10th, our flyer crew hit the streets of Austin and what commenced was the best pasting campaign in the history of SXSW. They absolutely fucking killed it. Papawaseverywhere. Even though I couldn't make it to Austin, I immediatley started receiving countless tweets, texts, and photos from my friends who saw the poster. Hell, Jason Feinberg even ripped one down as a souvenir since he's such a fanboy.
There were so many great reactions on Twitter to the posters I couldn't possibly post them all, but here are a few gems.
In addition to blanketing Austin in paper, we ran a complimentary Internet campaign at the same time from papaemeritus.com. The idea was to use the Papal Conclave tradition of casting paper ballots with the Latin heading “Eligo in Summum Pontificem” (“I elect as Supreme Pontiff”) to give all visitors the chance to channel their inner Cardinal and vote for Papa Emeritus II. This scheme also came with the added benefit of pulling an email address and some location data from the user for future mailings via the Facebook Graph API.
Once the fan's ballot was cast, it activated the Year Zero ritual which allowed them to stream the band's newest track. If you haven't heard it yet, go right now to unlock it and be prepared to witness one of the most fucked up videos I've seen in a long time very soon… I seriously couldn't watch it alone in my new apartment at night.
In addition to the site, we had an opportunity to react to the actual election progress on our Twitter and Facebook by demanding a recount when Pope Francis was elected. Side note: my buddy Pip suggested we come out and say Pope Francis was actually Papa without his mask on, but I didn't think I could get that one approved…
Post Mortem
I feel like the campaign could have been even bigger with more varied competing reactionary content, such as video, live stream from a fake papal election, or photoshopped photos of Papa in social situations. More parody, basically.
However, it goes to show that some old school tactics with new school execution can be quite successful even in over saturated situations if you can develop the right imagery with the perfect timing and context.
Thanks for reading. You can expect more from our Ghost B.C. campaign in the next few weeks leading up to the Infestissimam release. Follow me on Twitter for the latest and please let me know if you have any questions or comments.
When Jo and I started planning The Endless Hack, we tossed around the concept of “extreme” hacking. That is, creating software in locations that lacked the ideal infrastructure of power and fast internet. This idea was sparked by Chapelle Saint Michel, a tiny little church on an island in the Bretagne of France (where we launched the project) which was only accessible twice a day due to the extreme tidal conditions of that region. We thought it would be fun to go out to the island and attempt to hack something inspired by the church within the tidal timeframe the English Channel provided. We knew full well that there wouldn't be power on the island and chances of pushing code were pretty slim. However, I believe constraints are important in hacking because they keep you focused on producing an actual product in the time provided.
This idea is one of the main reasons we chose Punta Del Diablo as our first offsite, and while it wasn't quite as extreme as our church idea, it did present several challenges that made for a pretty interesting hack.
We borrowed internet from our Airbnb's next door neighbor [1]. Network name: gustavo. Password: puntadeldiablo00. Our hosts said it was “okay,” but I don't think they realized just how much internet we would be hogging and in some cases Uruguyan internet providers cap your internet usage and charge overage fees. Maybe that's why on the last day the network name was changed to “inside the dog” and was given a new password.
The signal?
Oh, it was shit, and led to a pretty funny power dilemma. You see I'm on a MacBook Pro with retina and Jo has an original MacBook Air, so we have two different types of power connections (MagSafe 1 vs 2) which require a specific power converter to fit Uruguay's outlets. Which we only had one of… That meant we were constantly reconfiguring our power setups to keep our laptops charged. What made this even worst is that the “good” internet signal, out on the balcony in a direct line of sight to the neighbor's router, was about 2 meters out of range from the maximum stretched length of our power cords. That meant you had to chose what was more important: power or internet, and reconfigure / reposition accordingly. Also, anything beyond just pushing code required us to literally walk up to the neighbors window and hold our laptops to it. This led to a lot of dead laptops and botched uploads.
Did I mention my trackpad died? Yes… Apparently I spilled some sort of liquid (I'm guessing beer) into it back in Montevideo. Luckily I brought my mouse, but that meant in addition to the power issues above, I also needed to find a flat surface to work from. I rotated between the bar downstairs and the deck of our balcony while Jo spent 90% of his hacking from the hammock.
Once we nailed down the idea and established all limitations on technology, we were ready to begin hacking. We had less than 24 hours to build the app, document it, and present it to Stockholm.
[1] Speaking of extreme, this neighbor owned an old VW van which he apparently drove down to Punta Del Diablo from Canada.
We needed some dinner and inspiration to come up with an idea for our hack, so we took a long walk over sand and rocks, pass the three major beaches, La Viuda, Rivero, Los Botes, and into the small but bustling city center of Punta Del Diablo. It consisted of many colorful bungalows, shops, markets, and bars distributing their wares in Uruguyaun pesos. One thing the graphic designer in me immediately noticed, were the large amount of beer sponsored store signage. I didn't feel compelled to enter places sponsored by Cerveza Patricia when they were next door to an establishment with a hand-crafted sign. I think Aaron Draplin would agree.
Speaking of advertisements, Punta Del Diablo employs a crew of mobile commercials which consists of a guy on a ATV carrying a speaker four times as large and about 10 volume notches too loud, spewing the same fucking 30 sec ad over and over again. It always starts with a little bit of Pitbull, Don't Stop The Party…
Ya'll having a good time out there? (drop) Yeah, yeah, yeah, que no pare la fiesta
Right into a spoken word segment:
Come, come, come! Milenesas! Capachino Caliente!
Etc… Repeat.
We didn't make it to whatever place this four wheeler was advertising, but the driver did seem to stalk us around the island like a bad Twilight Zone episode. Wherever we'd go, he, Pitbull, and capachino calientes would be right behind us, making sure we were not having a good time. Yeah, yeah, yeah…
We sought shelter from the audible chaos, and found a nice little cafe called Balvini or some other whiskey, which served small rectangular pizzas and cervesas in liter form only. After a few liters of beer, things got sloppy and I realized something amazing about my new Field Notes memo pad: they were water proof. Tear-resistent Yupo Synthetic Paper to be exact. This came in handy during sporadic rain spells and consistent self spills.
Beer turned to whiskey as day turned to night, and we became friendly with a group of middle aged women as a band began to setup within the cafe. Our waiter joined into the conversation which consisted of a comical bashing of my United States upbringing due to our sometimes ridiculous immigration laws. While I gladly took the blame for everything American in my orange camo hat, Johannes took credit for everything German including the wonderful city of Berlin, which everyone loved to bring up. Our advances with the women were cut short when we were forced to make a dash inside the cafe and lose flirtation proximity due to rain.
The night reached a climax when we began to pay more attention to the band which was now sound checking. They went at it for what seemed like hours, and by it, I mean sound checking. The band was led (whether or not the other members approved) by a bass player with extremely rockin hair whom kept wandering the stage and street perfecting all possible acoustics. I'm guessing for the bass part. When the set finally began, he did not disappoint and head banged (bung?) through the entire gig, constantly taking center stage where I recall a single spot light pointed.
Our amazement for this nameless bass player turned to inspiration when we agreed to try and build a game that incorporated head banging. We stumbled home to get some rest and prepare for the hack mañana.
We arrived in Punta del Diablo on Friday evening by way of a WiFi powered COT bus traveling the vast nothingness of Highway 9 on the Uruguayan coastal highway. And by nothing, I mean one tree for every square mile of plains and hills as far as you could see. Occasionally the bus would come to a stop on the road or in a village and locals would silently jump on, because the driver kept the door opened for the entire journey. I would lower my head to get some writing done, and the second I raised it three new passengers would be standing in the aisles right next to me, as if they were all in on the prank.
The trip took around five hours to make it from the Tres Cruces in Montevideo to the bus stop in Punta Del Diablo where Martin, one of our Airbnb hosts was waiting for us in his 4-wheel drive. While I did refer to Jo as my “partner” in the room request, I think it was plain to see from Martin's realization of just how straight we were, that we would rather not share a bed and a misfortunate misreading of the B&B listing on my part (double bed does not mean two beds) would have Jo and I sharing a mosquito net later. Oh, well.
A quick drive down a labyrinth of dirt roads, which Martin assured us would be navigable by foot at night no matter how drunk we ended up, led us to Terraferma, Alessandro, Martin, and their dog's home, and ours for the weekend. The view from the front porch, given our close proximity to the beach and Parque Nacional de Santa Teresa, not to mention the city of hippies and tents we passed on the way end, reminded me a lot of Big Sur.
We entered our new home and were greeted by our non-english speaking Brazilian roommates, a guy and gal, both fresh from the beach who I would imagine also assumed Jo and I were a couple. However, I don't even think English could have saved us when we tried to explain to them that we journeyed all the way to the north coast of Uruguay from Los Angeles and Berlin to work.. on our computers.. and participate in a hacking competition happening in Stockholm.
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 shockinglyaccessible 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
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.
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.
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 = () ->
loadedSounds++
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
soundManager.createSound
id: sound.id
url: "/mp3/#{ sound.id }.mp3"
onload: ->
aSoundLoaded()
whileloading: ->
aSoundLoading @id, @bytesLoaded / @bytesTotal
.load()
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: ->
tickClock()
for stem in _.where(sounds, { stem: true })
soundManager.play(stem.id).mute()
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.
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)"
$('#hand').css
"transform": rotate
if playing is false
if tick%2 is 0
soundManager.play('TICK')
else
soundManager.play('TOCK')
, 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.
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:
Converted?
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.
Last weekend I traveled to Punta Del Diablo for a bit of code in the sun and to remotely participate in Music Hack Day Stockholm. As per usual, Johannes and I decided to build a video game, BeatStriker, in under 24 hours and submit it to the competition.
Today, I'd like to share some technical details on a simple client side WebRTC sprite sheet generator I built. We used it to define a simple nine grid sprite sheet consisting of a user's mug facing nine directions, using a WebCam and a tiny bit of client side JavaScript.
You can expect another blog regarding Punta Del Diablo and the pros and cons of hacking from a hammock soon.
Sprites
A sprite sheet is a series of images (sprites) that usually correspond to individual animation frames which are combined into a larger image (or images.) Game designers have used these since 1974 to limit the amount of assets required to make players and elements move, and web developers have used them since… well, I guess since the CSS background property. Animations are represented by displaying one frame at a time in a pre-defined sequence.
My first run in with sprites was in 1999 when I participated in the Mugen gaming community. Mugen is basically an open-source Street Fighter which allows developers to define their own sprite sheets of playable characters and levels. This allowed for some pretty weird match-ups like Ryu vs Homer Simpsonaka copyright infringement, but what the hell else was I going to do while waiting for all my Napster downloads to finish?
A new challenger appears!
Prerequisites
The only HTML and CSS we need to pull this off is the following three elements and a defined size for the #sprite div.
You'll also want to include Addy Osmani's excellent getUserMedia.js library and Underscore because I was lazy about some selectors.
Variables
Next we'll need to define a few variables. photo keeps track of which picture we are currently taking. size defines the size of each sprite image, and should match the #sprite div sizing above. The directions array defines how each sprite should be placed on the sheet given the grid sizing. Each hash also includes an i (info) variable which will be used to display user instructions during the picture taking process.
If you read this blog, you'll know I'm no stranger to WebRTC or getUserMedia.js as I've used it before in my Foo Fighters In Rock We Trust hack. Sadly, there hasn't been great leaps in adoption since that post and we're still stuck in Google Chrome only. Unless of course, your user is running Firefox nightly. Hint: he isn't. So if you plan on going into production, you'll need to utilize the getUserMedia.js flash fallback function. I tend to think you're a dummy for not using Chrome (and this was a hack) so I won't be covering that here.
Let's start by configuring our getUserMedia shim, mapping a few jQuery selectors, and defining what happens when the WebCam successfully streams or throws an error.
Next we'll define a function which takes each photo by resizing and cropping the captured WebCam video and then drawing it onto a canvas at the relavant location using the directions array. The photo variable is then incremented to allow for the next picture to be taken.
Once all eight photos are taken, we can use the toDataURL() function canvas provides to apply the sprite sheet to our #sprite element's CSS background.
takePhoto: () ->
if photo < 8
v = document.getElementsByTagName("video")[0]
h = v.videoHeight
w = v.videoWidth
x = (w - h) / 2
d = directions[ _.keys(directions)[photo] ]
App.context.drawImage(v, x, 0, h, h, d.x, d.y, size, size)
if photo isnt 8
next = directions[ _.keys(directions)[photo + 1] ]
App.instructions.text("Look #{ next.i } and press spacebar.")
photo += 1
else
App.sprite.css
"background": "url(#{ App.canvas.toDataURL('image/png') })"
App.instructions.text("Done! Try pressing your arrow keys.")
Controls
Now that we've created our sprite sheet and associated it with our #sprite div, we can define some basic controls to animate the face into pointing a particular direction. First let's write a simple function, which takes a d (direction) and shifts the sprite sheet's CSS background positioning given the appropriate coordinates from the directions array.
Then we'll write a simple jQuery keydown function which calls the takePhoto() function each time Spacebar is pressed, and the pointFace() function each time Up, Right, Down, or Left is pressed. When the user let's go of a key, the face returns to center.
$(window).keydown (e) ->
switch e.keyCode
when 32
App.takePhoto()
when 38
App.pointFace("north")
when 39
App.pointFace("east")
when 40
App.pointFace("south")
when 37
App.pointFace("west")
$(window).keyup (e) ->
App.pointFace("center")
Note: We ended up rewriting this to detect mouse direction instead which allowed us to call the diagonal points also.
Finish Him!
If you made it this far, you're ready to initialize your app:
App.init()
Refresh your browser and here's what should happen:
Google Chrome will prompt you to allow WebCam access.
Once allowed, your beautiful face will appear in the #webcam div.
Follow the #instructions by facing the appropriate direction and clicking Spacebar to generate your sprite sheet.
Once that's done, you should be able to control your face with your keyboard arrows.
From here, you may want to save your newly created sprite sheet to the server for future use or add some gaming elements like we did for BeatStriker.
Check out the full source on Gist and as always, let me know if you have any questions or suggestions on Twitter. Happy coding.
The Endless Hack has begun and what a start it has been! I'm currently writing this tutorial on a Buquebustimezone crossing Rio de la Plata, the Silver River, from Argentina to Uruguay. We chose Buenos Aires as our first destination because we didn't know anyone or what to expect and our first hack, Who the ****s in Town?, assisted us in making some contacts by indexing our Twitter social graph geographically.
In order to accomplish that, we pass each of our followers' profile location through a geocoder to improve search results. This takes a bit of time so I created a slot machine which users can play while waiting for each query. I was inspired by Tweetie's slot machine announcement when Twitter bought their iPhone app and made it the official client “back” in 2010.
While Three.js was a tempting path to take, I decided to merge two tutorials I found on Google, CSS3 3d Primitives and CSS3 Slot Machine, into a simplified app using CSS3, HAML, Sass, Compass, CoffeeScript, and Symbolset.
Before I go into that let's discuss the slot machine.
History
Original slot machines had 5 physical reels but simpler, 3 reel machines became the standard due to their reliability. The only inherent problem with 3 reel machines is that the combinations are cubic. So, if you have 10 symbols on each reel there would only be 10³ or 1000 combinations. Meaning the manufacture could only offer jackpots 1000 times the original bet, knowing that it should only happen 0.1% of the time. On the other hand, virtual reels can have any number of stops per reel and their combinations and jackpots can be in the millions. However, we'll be basing our simple app around the classic 3 reel physical design as we won't be offering any jackpots.
HTML
Using Roman's tutorial on cylinder construction, we can create the reels. First we create an array of Symbolset objects which we will use to define what symbols are displayed on each face on the reel. I chose symbols that represent networking with people like a city, briefcase, and yes: beer. Then we have to create three sets of divs which each include the ten symbol faces in addition to placement and transformation divs:
Each of our reels are 10 sided cylinders created completely in CSS3. Rather than go through every line of my code, let's look at some of the key points and I'll include a link to the source at the end. One thing to note, is that each line of my code below has a + pretended to it because that identifies the related cross-browser Compass mixin.
First, our HTML contains multiple container divs that relate to how each transformation is presented so let's begin by preserving that in 3D and hiding the backs of each div.
Next, we'll set the center of rotation for each .reel-face-outer and use a Sass @while loop to rotate each 36deg on its X axis creating the spokes of our slot machine.
Finally, we center the transform of each .reel-face and rotate each 72 deg from the spoke to create our cylinder surfaces. Each should also be given a line-height equal to its defined height so that Symbolset symbols appear centered.
JS
Once that's all setup, spinning the wheel itself is pretty trivial with one tiny caveat: progressing the rotation value. Since we're doing all of this in CSS3, we can't really say: spin 360° and then spin 360° again, we have to say spin 360° and now spin 720°, adding to the total rotation with each spin. I'll break down the CoffeeScript in the comments below:
$ ->
result = []
count = 0
# Loop through each reel
$('.reel-outer'). each ->
$this = $(this)
index = $this.index()
spinPlus = 0
# When the spin link is clicked...
$('.spin').click ->
# Choose a random symbol between 1 and 10
type = parseInt((Math.random() * 9), 10) + 1
# Define a spin duration between 1 and 10000
duration = parseInt((Math.random() * 10000), 10)
# Increment the total spin value
spinPlus += 3600
# Multiply type by 36 (degrees) and add to the total
spin = type * 36 + spinPlus
# Apply the transition duration and rotation
$this.find('.reel').css
WebkitTransitionDuration: "#{ duration }ms"
WebkitTransform: "rotateX(-#{ spin }deg)"
# Add the reel result type to array
result.push(type)
# Increment the spin counter
count++
# Once the counter reaches 3...
if count is 3
# And all transitions are ended, display results
$this.on "webkitTransitionEnd transitionend", () ->
console.log result
count = 0
result = []
Jackpot Party!
Anyway, it was a fun hack and goes to show just how much you can accomplish with CSS these days. Check out the full source on Gist and follow The Endless Hack for more world-wide hacking goodness.
And as always, let me know if you have any questions or suggestions on Twitter. Thanks for reading!
2013 marks the 10th year I've been working in the music business.
10 fucking years.
What started as a chance to pay my rent while at LSU by making buddy icons for Sonic Youth has evolved into an obsession, a way of life. I went to college for art and drew my way through it. Well… almost. I knew early on that all I wanted was to make sure my work was seen and the artists I've worked for over the years have given me that opportunity. There's no way I could thank all of them here, but Mars Volta, Jimmy Eat World, AFI, and Foo Fighters deserve a special shout out. Thank you guys.
I wouldn't have gotten here without my mentor and best tattoo'd lady friend: Kristen. She tapped me when I was just a lowly fan site owner and helped me make the leap to official site developer. She understands the basic concept that hard working fans, no matter how unlikely their location, deserve a chance to participate. And it's not just about using hardcore fans for your bidding, it's about cultivating that relationship and understanding that particular fans' perception of the artist as a campaign. Thank you Kristen. Swap meet?
I also have to thank my “boss for life” John Silva and his amazing staff at SAM for making me a part of their family and spoiling me with the best job in the music business, even though I only lasted two years. I think I go into the office even more now that I've quit (and not only for the free lunches), because one does not simply leave the Samily… Speaking of ex-co-workers, danke to my friends at SoundCloud like Eric, Robb, and countless others for helping me take web development much more serious and introducing me to the world of open-source, international travel, and collaboration.
I know, this is starting to sound like an acceptance speech. Next up I'll thank my loyal fans and the Lord, but the truth is: I'm very lucky and I can't thank all of the folks above, artists, and my network of colleagues for their guidance and continuous inspiration over the past ten years. I owe each of you a cold beverage to be redeemed at a future wasted night out of your choosing.
Something I would have done differently
This. Sharing stories and open-souring the knowledge I've gained.
For a large part of those ten years I kept everything I did very secret, thinking that my techniques and the ways I executed on projects were my biggest asset. This is partly true, but I was looking at it in the wrong way.
Not only does sharing free yourself of your ideas to make room for new ones, it also encourages your peers to share not only their opinion of your thoughts but ideas of their own. In fact, every time I post an idea, I'm constantly greeted with several new ones. If only I would have started doing this earlier!
Johannes and I were in France a few weeks ago launching a new project we've been conspiring the past few months: The Endless Hack. There's longer prose in the works regarding that, so in the meantime please sign-up to the newsletter and follow the project on Twitter and Facebook. On today's blog I'd like to tell you a story from our time in Paris.
Planète Mars
Rather than go out our first night, we holed up at the closest bar next to our Airbnb: Planète Mars. You enter Mars on the left and are greeted with its semi-round bar surrounded by an array of different stools on your right. The bar itself is fairly small but makes up for its size in quirks:
Monday is Scandinavian DJ night
There's a Glen Livet holder, holding a bottle of Jim Beam
A €45 bottle of Champagne offered via a hastily scrawled advert
Small glasses for newcomers, big glasses for regulars
A little green woman figurine being lit by a lamp for no apparent reason
It's all helmed by Hakim Meziane, the bartender / DJ. Not only is Hakim extremely welcoming and funny, he also has incredible taste in music, playing an indie friendly playlist which bounces nicely off the walls filled with rock photography and posters. He pointed out a sign above the bar which proudly supports a 100% price increase for hipsters. Worried, I asked about myself, considering the fact I was wearing a sweater with two kittens on it. He said, “I was just waiting for you to take a picture of your drink.” I laughed.
La Fraction
Johannes stepped out for a bit and left me alone to chat with the bartender when in walked the first barfly: Christoph. I sat quietly while he and Hakim exchanged a few words in French and then politely acknowledged him with a very American, “Sup?” Since I don't speak any foreign languages, I tend to act like a dumb American when at all possible both in appearance and initial dialogue. In my experience, everyone speaks English and I have very little chance of being lost in translation.
Johannes returned soon after and we started chatting up Christoph. It turned out that he was in a French punk band, La Fraction and frequented Planète Mars regularly. We discussed a range of intoxicated music topics including Christoph's dislike for people recording his shows. As it just so happened, Johannes and I had hacked, OMGIG, an app related to this exact issue at Music Hack Day Reykjavik. After a drunken deliberation, we decided to demo Hakim and Christoph our app at Planète Mars.
We explained that our initial perception of the issue was very similar to Christoph's in that recording the show through your iPhone instead of enjoying it was pretty lame. However, during development we realized that we wouldn't be able to achieve the effect we wanted unless the bootleg video existed in the first place. We were conflicted.
Hakim and Christoph loved it, and we continued our seesaw of drinking Edelweiss and whiskey into the early morning or way past Christoph's “time limit.” I could go further into what Christoph does for a living or tell you about his incredible business idea, but instead, here's a video of his band performing “Help Yourself,” via OMGIG of course.