Show contents

Soundication

SoundTube

or on converting your audio into YouTube videos

When it comes to sharing music on the web, two factors are of the upmost importance: accessibility and syndication. I've preached a lot about making sure your content is accessible from any device (using HTML5 capable players and a clear play button) in the past, so today I'd like to talk about syndication, and making sure you're reaching as many ears as possible.

These days an artists' fans are distributed around the web in multiple destinations. Gone are the days of centralized traffic hubs like MySpace and yes the band website. So instead of funneling fans into a single destination of content, artists are syndicating their assets to their site, social networks, and most importantly (IMHO) email.

It's for this reason we also see the repetition of content on media platforms such as SoundCloud and YouTube. The truth is, in order to maximize your plays and exposure, you need to distribute your content as wide as possible. This is why you'll some see some videos on YouTube that only consist of a single image and audio track. While it may not be the most appropriate use of a video platform, it does work. In fact, I premiered the Them Crooked Vultures record this way complete with annotated track navigation and was very happy with the results.

So, if you were going to create a “sound” video, how would you go about it?

Google? iMovie? After Effects? (I'm looking at you Alec)

Naaa, dawg. Just use…

SoundTube

SoundTube is a simple app that can convert your SoundCloud tracks into simple YouTube artwork videos. This allows you to rapidly extend the promotion of your tracks to another community of listeners.

Simple is an understatement, here's how it works:

  1. Login to the YouTube account you wish to upload to.
  2. Login to the SoundCloud account you want to pull a track from.
  3. Select a track and click the convert button.

That's it. Your video will now be queued up for conversion and uploaded to your YouTube account as soon as it's ready. While the conversion process takes about 5-10 minutes, logging in and selecting a track only takes about 30 seconds of your time. Use the time the app saved you to sneak a Modelo midday when your boss isn't looking.

With simple apps, comes simple technical solutions, so let's take a look at how SoundTube works.

What the FFMPEG?

I've covered my love for server side media processing before, but to reiterate: I like the idea of using a server to create new customized content like photos and in this case, video. For images we can use ImageMagick to do all sorts of manipulation but for video we're going to be utilizing FFMPEG.

Given an image and audio track, how can we use the library to forge these two assets and create a new video? Searching Google for “FFMPEG” will result in all sorts of tutorials, most of which will be about video format conversions, resizing, and thumbnail creation. Not exactly what we're looking for. Search a little deeper and you may uncover this function:

ffmpeg -loop 1 -i image.jpg -i sound.mp3 -shortest video.mp4

And, that's exactly what SoundTube is using. Let's break it down:

Loop through the first input -i, which is a JPG image, continuously while combining it with the second input, an MP3 track, to create the -shortest possible MP4 video. Pretty cool.

Once the video is created, you can use the YouTube_It Ruby gem to upload to the preferred user's YouTube account.

Since each conversion process is resource intensive and takes a bit of time, I've chosen to throw these into my background queue of choice: Resque. Resque allows you to declare simple models that contain your processes. Adding one of these to a queue will store it temporarily on Redis so that it may wait to be processed. Timing depends on the amount of workers you have running.

Moar

There's a few things I wanted to do but haven't had enough time yet, such as making the conversion process more transparent. Resque provides a job id for each queued process, so we could technically ping Redis for that current job's state. You could even estimate the time until conversion.

I also think it might be cool to expand the FFMPEG technique to include multiple photos and text such as lyrics. Combine that with a nice UI and some Ken Burns effects, and you've got yourself a nice simple video creator.


Anyway, try it out and let me know what you think via email or on Twitter.

 
38
Kudos

Sim Bootleg

OMGIG

On the art of bootlegging live concerts and YouTube WebM video.

This post goes out to all the bootleggers out there, sacrificing their own concert experience so we can relive our favorites moments on YouTube later. Their efforts, for the most part, have gone under appreciated. Until now.

During this weekend's Reykjavík Music Hack Day, Johannes and I built a modern concert bootleg simulator called OMGIG. The premise is simple: Take any YouTube video and see it through the bootlegger's own eyes, that is the eyes behind a video recording iPhone.

Instructions

  1. Search for any YouTube video.
  2. Change youtube.com in the URL to omgig.com
  3. That's it!

You will then be transported back in time to the faithful day when that brave bootlegger forfeited his own vision to maximize your future viewing.

Inspiring!

How's it work?

In general, the app is quite simple and you can find a list of all the technology used on the Music Hack Day wiki. However, there was one really interesting technique that came out of development which Jo has since turned into an open-source JavaScript library.

Placing a YouTube video into the iPhone graphic is easy enough, but what about blurring the background image? That's where things got a little hacky.

I knew we could blur a video by placing it into a <video> element and sending it to an HTML5 canvas for stack blurring or by calling the CSS3 blur filter. But, how were we going to get a YouTube file into the <video> element? We know that YouTube has been creating WebM versions of all new uploads for their HTML5 players and that particular file format is made for the <video> element, but they don't exactly make it clear as to how to access that in external apps. I'm guessing that's because they don't want you to…

Oh well. ;-)

After a lot of googling, we figured it out. Apparently, you can make a call to YouTube's get_video_info end-point with a video_id and receive a list of video sources in addition to basic meta data. Then you're just a bit of URI decoding away from uncovering the WebM source url. Hell, yeh. Rather than force you to do any decoding, Johannes extracted his source into a handy little library available on GitHub called YouTube-Video.

Go ahead. Fork it. We dare you.

We're not entirely sure how this works as we should be running into cross-domain issues… perhaps CORS? Jo is still researching… What we do know is that this opens up the possibilities for all sorts of YouTube manipulation, especially when you draw the video element to canvas:

// Get DOM elements
v       = document.getElementById('v');
canvas  = document.getElementById('c');
context = canvas.getContext('2d');

// Set sizing
cw = 640;
ch = 360;
canvas.width  = cw;
canvas.height = ch;

// Listen for the `<video>` play event
v.addEventListener('play', function() {
  draw(this, context, cw, ch);
}, false);

// Draw it to `<canvas>`
draw = function (v, c, w, h) {
  c.drawImage(v, 0, 0, w, h);
  setTimeout(draw, 20, v, c, w, h);
};

So…

…check out the inspiration and use the library to make your own YouTube hack. If you do come up with something cool, please let me know on Twitter.

Good luck!

 
38
Kudos

Free Instagram Printer

Insta Printer

A tutorial, an app, and a cause. By Lee Martin.

A few weeks back I launched the In Rock We Trust campaign [1] for the Foo Fighters. At the end of that post, I mentioned that I wrote some software which printed the customized bills in real-time as they were minted on the site. I also mentioned that you could use a similar technique to create your own Instagram printer.

But what is an Instagram Printer?

New York based creative agency, Breakfast, pioneered this technique in a Kickstarter project they dubbed Instaprint. The basic concept was this:

Instaprint prints out Instagrams by watching out for any photos tagged with specific location or hashtags.

So, let's say you're throwing a Halloween party next week, and you've told all your friends to tag their Instagram photos #haleeween2012. You could technically set up a printer to be printing off these Instagram photos in real-time as they're taken, effectively creating a photo booth of sorts for your friends. Pair this with a nice compact photo printer and you're creating a keepsake for every attendee. Pretty damn cool.

The Breakfast Instaprint campaign turned out to be a bit too ambitious and their Kickstarter was unsuccessfully funded. They resorted to renting the device for around $5,000 a day, ouch. I want to make an analogy here that you could buy Instagram for $5,000 but we know that isn't true. However, that is still quite a bit of money and out of the reach of most everyone.

So, when trying to devise a solution for the In Rock We Trust minter, I stumbled upon a technique that could easily emulate this concept with almost any printer and operating system for free. The solution I'm offering comes in two parts:

  1. An explanation: Any developer can make use of this blog post to create their own Instagram printer or any sort of API based printing application.

  2. An app: Any user can use my Insta Printer app to set up their own Instagram printer in a few minutes, but I'll need your help to raise some limitations I'm facing. More on that later.

In the style of Jock Jams: Ya'll ready to print?

Insta Printer Explanation

Before I get into any specifics, let's take a look at a high level overview of how my Insta Printer app works:

Instagram > Insta Print > Cloud Print > Chrome > Printer

  1. An Instagram photo with your specified tag is taken.
  2. The Insta Printer app responds to a realtime update from Instagram and sends the photo in the form of a print job data to Google Cloud Print.
  3. Google Cloud Print creates the print job with specified media and target printer then proxies the print job over to Google Chrome.
  4. Google Chrome then sends the print job over wifi or ethernet to the printer.
  5. Finally, the printer receives the job and prints the photo.

In order for all of this to work properly, you must be signed up to and have your printers connected to Google Cloud Print and have a properly setup, logged in, and open copy of Google Chrome running.

Sending Print Jobs to the Cloud

Wait, what the hell is Cloud Print?

Google Cloud Print is an under appreciated service offered by Google that connects your printers to the web. As they put it:

Using Google Cloud Print, you can make your home and work printers available to you and anyone you choose, from the applications you use every day. Google Cloud Print works on your phone, tablet, Chromebook, PC, and any other web-connected device you want to print from.

One common misconception of Google Cloud Print is that it only works on Cloud Print enabled printers. This is false. In fact, Google Cloud Print will work with just about any Internet connected printer as long as it has a clear line of communication to an open copy of Google Chrome. Google likes to refer to these non-cloud enabled printers as Classic Printers.

Guilty as charged. Let's set one up. [2]

Setup a Classic Printer

  1. Download and Install Google Chrome.
  2. Open Google Chrome and then the browser's settings. ⌘+,
  3. Click the Show advanced settings link.
  4. Scroll down to the “Google Cloud Print” section. Click Sign in to Google Cloud Print.
  5. In the window that appears, sign in with your Google Account to enable the Google Cloud Print connector.
  6. When the confirmation message appears, click Finish printer registration.
  7. Google Cloud Print has been enabled.

You can test your setup from the bottom of Google's instructions by clicking the Print a Test Page link.

API Service Interface

Google also offer developers an API to automate print job submission, and therein lies the magic of this service. All you need is a Google OAuth2 access token with the appropriate permissions.

I won't go into details of how to obtain an access token from a Google user as this has been documented before, but in general I use omniauth-google-oauth2 with the following provider / permissions setup:

use OmniAuth::Builder do
  provider :google_oauth2, CLIENT_ID, CLIENT_SECRET, { scope: "cloudprint,userinfo.email", access_type: "offline", approval_prompt: "force" }
end

Once you have an access token, the first thing you'll need is a printerID for the device you wish to use. The best way to obtain this is to present a list of printers on the front end so the user can choose. Here's how to pull a list:

access_token.get("https://www.google.com/cloudprint/search")

Then, with the printerID, we can easily submit a print job:

access_token.post("https://www.google.com/cloudprint/submit", params: { printerid: PRINTER_ID, title: "Insta Print", content: PHOTO_URL, contentType: "url" })

Now that we know how to print, let's work on obtaining what we want to print: Instagram photos.

Kicking it Real Time with Instagram

Instagram offers a real-time API based on the PubSubHubBub protocol which allows your app to subscribe to hashtags, geographies, locations, or users and be notified when any new associated photos are posted.

The only prerequisite for setting this up is registering an Instagram application. Once you have your app client key and secret, you can setup your Instagram client. I like to use the Instagram Ruby Gem to do this.

Instagram.configure do |config|
  config.client_id = CLIENT_ID
  config.client_secret = CLIENT_SECRET
end

Instagram has nice documentation on subscribing to the appropriate object and responding to the incoming updates, but here's a very simple example of how I'm doing it with the Instagram Ruby Gem on Sinatra.

// Create a tag subscription, the callback url should accept both GET and POST requests
get '/subscribe' do
  Instagram.create_subscription("tag", CALLBACK_URL, "media", { object_id: TAG })
end

// Verify your subscription by echoing back the hub.challenge
get '/subscribe/callback' do
  request.params["hub.challenge"]
end

// Receive updates
post '/subscribe/callback' do
  Instagram.process_subscription(request.body.string) do |handler|
    // do something
  end
end

One thing to note is that the changed data, that is the actual photos, are not included in these updates. It is merely a notification of new media. I'll explain how to fetch these new photos in the next section.

Instagram + Google Cloud Print == Magic Trick

It's an illusion, Michael.

Now that we know how to send print jobs to Google Cloud Print and subscribe to real-time photos from Instagram, finishing our Insta Printer is easy. We simply combine the two techniques on the subscription callback method. Take a look at the code below:

post '/subscribe/callback' do
  Instagram.process_subscription(request.body.string) do |handler|
    handler.on_tag_changed do |tag|
      photos = Instagram.tag_recent_media(tag).data

      if photos.length != 0
        for photo in photos
          access_token.post("https://www.google.com/cloudprint/submit", params: { 
            printerid: PRINTER_ID, 
            title: "Insta Printer", 
            content: photo["images"]["high_resolution"]["url"], 
            contentType: "url"
          })
        end
      end
    end
  end
end
  1. Process each incoming subscription by passing the request body to a tag specific handler event.
  2. Fetch recent photos from Instagram which contain the required tag using the tag_recent_media function.
  3. If photos exist, loop through each and submit the high resolution photo url as a print job to Google Cloud Print.

Instagram will then update your app each time a new photo is posted with the subscribed tag, and your app will pass the photo's url off to your Google Cloud printer. Hell yeh.

Insta Printer App

Insta Printer App

So that was a high level explanation of the technique I'm using. For those of you who don't feel like programming, I've combined these features in a simple turn-key app called Insta Printer.

Insta Printer is an app that allows anyone with a Google account and Google Chrome to setup a printer that prints Instagram photos posted with a specific tag in real-time.

The only prerequisites for using Insta Printer is installing Google Chrome and setting up Google Cloud Print.

Instructions

Once the prereqs are met, fire up Chrome, go to Insta Printer, and do the following:

  1. Login with your Google account.
  2. Select the printer you wish to use.
  3. Choose an Instagram hash tag.

And, that's it! You have successfully created an Instagram printer. Now all photos tagged with the tag you specified will print out on your printer.

However, there is one…

Problem

Instagram restricts all apps to only 30 real-time subscriptions at any given point. This limitation will only allow me to make this app available to 30 users at a time. Bummer. One possible solution, would be to allow each user the ability to provide their own Instagram client id and secret, but that adds another step I'd like to avoid.

So what can we do?

The Cause

We gotta take the power back!

Well, we can try to get Instagram to whitelist my app so that it doesn't run into limitations. That way, we can all, have Instagram printers for Halloween!

By the time this blog gets out, I hope to have their attention, and you can show your support by taking an Instagram and tagging it #freeinstaprinter, tweeting, and upvoting.

If that doesn't work out, I'll open-source the app here as soon as I get a second to package it up for deployment.

Follow me on Twitter and I'll let you know how that goes.

Conclusion

Is this the best way? Probably not, but I do like that it is software only. Regardless, I think this is a testament to the power of Google Cloud Print. You could use a similar technique to create a Little Printer clone and yes, even a Foo Fighters minter.

Please let me know if you have any questions or comments on Twitter or via email hi@leemartin.com.

Happy printing!


[1] Check out that campaign here and read the behind-the-code blog here.

[2] Here's Google's extended instructions on connecting your classic printer to Google Cloud Print.

 
138
Kudos

A Music Business Job Board

Sex, Drugs, & Jobs

I got 99 problems but a job ain't one.

I get asked two questions quite a bit

Lee…

  1. Do you know someone who can fill this position?
  2. How do I break into the music business?

The answer to the first is yeah, probably. I tend to surround myself with nerds and creatives. And the second is I don't know …. work hard for a band you care about and hope for the best?

The truth is, recruiting, being recruited, and just getting a chance to work in the music business is tough. There's so many great jobs and talented candidates, waiting for the day they randomly bump into each other. Wouldn't it be awesome if the best jobs and talent were able to converge at a common location daily?

I wanted to solve this problem as efficient as possible for my own peace of mind so I decided to build an app: It's called Sex, Drugs, & Jobs and it's a job board for the business we hate to love: Music.

A job board… seriously?

Yes. Seriously. This is a topic I'm very passionate about! I mean, how can you not feel awesome about getting someone a killer gig? Or filling that sweet position with the perfect candidate? The fact of the matter is there are so many talented individuals out there who would kill to work for their favorite artists so let's all of do our part to seek them out and get them the opportunities they deserve.

But what makes this different from any other job board?

Sex & Drugs

For starters, a few basic things:

It's music business specific

Is there any other business really? Fuck sharing real estate with other real professions. If your job doesn't go to 11 , it doesn't belong here.

It will be curated

I've used my 10 years in the new music business to network with several different groups of people: developers, designers, marketers, industry leaders, management companies, labels, etc. I'm very fortunate to know so many great people, and I'd like to think that I can help bring all these worlds together.

There will be an edge to it

I remember recruiting several candidates to SoundCloud and doing it in my own style: getting people drunk in the middle of the day and convincing them to work there. Just ask Justin Street. I'd like to translate that drunken enthusiasm into this job board. That's why I teased the board with several edgy slogans, like:

The truth is, the recruiting process has to be fun and it cannot under any circumstances appear to be boring.

Me, duh

I plan on directing all of the traffic I generate personally via my website, projects, and my social accounts to the job board. Any future opportunities and talent that come through my inbox will be forwarded to it.

And, It goes without saying that I'm a really good spokesperson for the companies that I'm passionate in. I just can't help it.

Alright, you've convinced me.

How do I post my job listing?

For now, drop me a line and I'll personally get it added for you. Be sure to include the job listing, location, company name, and logo.

I plan on making the posting functionality public soon, but it will also be accompanied by a small fee. The funds generated to further the outreach of the job board to a select few partner websites. Help us, help you.

How do I get hired?

Check out the job board for all current opportunities. Each listing will have it's own application process, so be sure to read the highlighted area carefully.

To stay up to date with future listings and updates, make sure to sign-up for the newsletter, follow us on Twitter, and like us.


Please let me know if you have any questions, comments, or suggestions via email or on Twitter.

Thanks for listening and good luck!

 
48
Kudos

Brain Transplant

IT'S ALIVE

or How to migrate Postgres Databases with Heroku.

Last week I discussed some highlights from Johannes Wagener and my Battle Born campaign for The Killers. As mentioned in that post, we also simultaneously launched a campaign for Muse called The Social Connectome. Using the Human Connectome Project as inspiration, we developed a Chrome experiment that mapped and visualized Muse social connections (rather that the brains neural network) in an organized effort to unlock The Isolated System, a track off the new album, The 2nd Law. You can read an overview of that build on Jo's blog.

Today I'd like to tell you about something that transpired right in the middle of that launch and serves as a testament to Heroku's excellent Postgres database infrastructure.

Growth

The campaign itself was quite viral for two reasons:

  1. Muse has a shit ton of fans: over 13 million Likes and 1 million Twitter followers
  2. We created personalized entry links for some of the biggest media outlets such as NME and Rolling Stone so they could track their contributions to the Connectome. In other words, gamifying the press.

This led to several thousand concurrent visitors for the first few hours. All in all, the site held together nicely (after scaling up our Heroku dynos) because it was a single page client side app cached to all hell.

However, there were…

Complications

Johannes foresaw an issue that was going to arise because of how we created the unique media specific links. We generated these initially in our development database and serviced them out one by one to each outlet.

Once we were ready to push the site live, we wiped the database of all test data, including the media links, and regenerated them to the precise ID they were given before. This led to a slightly out of sync database index:

Given our setup, the first 100 users that created their own unique link wouldn't see any issues, but user 101 would run right into the first press link causing the database to throw an error due to duplication. In reality this point of contingency was more like 5,000 (not 101) so we had some time to react, and the fix was easy enough: shift the database index pass the press links.

However, we quickly realized that we weren't going to be able to do this because we had already hit the concurrent user connection limit on Heroku's Postgres development database: 20. In other words, the mass of activity on the site was preventing us from accessing our database as an admin. Doh.

Procedure

The only way to raise the database connection limit was to upgrade to a higher tier Postgres configuration offered by Heroku. We chose the base option, but the upgrade only got us half of the way there. We also needed to migrate all the current rows over to the new database.

Given the fact that the inspiration of this campaign was neural mappings, I penned this migration of data a brain transplant.

Operation

Johannes operated on our patient while I watched nervously from the operation deck. He began but putting the patient to sleep:

$ heroku maintenance:on

Then he captured a copy of the patient's memories:

$ heroku pgbackups:capture --expire

and transplanted them into our new brain:

$ heroku pgbackups:restore HEROKU_POSTGRESQL_FRANKENSTEIN

I watched him place the new brain into our patient:

$ heroku pg:promote HEROKU_POSTGRESQL_FRANKENSTEIN

and flip the switch.

$ heroku maintenance:off

He then whisper something, a prayer perhaps, and waited…

$ heroku restart

IT'S ALIVE

Please! Remain in your seats, I beg you! We are not children here, we are scientists! I assure you there is nothing to fear!

While I do love the theater of this occurrence, I should also mention that this could have all been easily prevented if we generated those links differently or started with an upgraded database. However, then we wouldn't have found out that Jo looks a lot like Gene Wilder.

 
57
Kudos

Runaways

Runaways

There comes a time in every programmer's life when he must seek out help in order to have a project (or projects) realized. That happened for me three weeks ago, and that help came in the form of Johannes Wagener.

Jo and I met two years ago while we were both working at SoundCloud, and quickly realized that we shared the same amount of enthusiasm for two things: alcohol and code. So naturally we ended up hacking several drunken apps together like Concert 2021, Wave Raid, and Waveform.JS. So before running away from SoundCloud, we vowed to hack on other fun projects in the future…

Well the future is now, and we've spent the last three weeks developing two campaigns together, one for The Killers and another for Muse. Today I'd like to share a quick overview and some of my highlights from The Killers Battle Born build.

Keep in mind that we are only two people, separated by nine time zones, solving two complex problems for two massive artists, in three weeks, with occasionally overlapping schedules… for that I'd like to thank Skype, Git, and a shit ton of Community Coffee dark roast. For Jo, probably bread and cheese

Anyway, let's runaway to

Battle Born

The concept for this campaign came to us in the form of a sentence “The Killers invite you to take their new album for a spin. Click here for a free ride” and some, sick, art from Warren Fu, The Killers incredible art director.

Here's exactly how we laid out the project goals in the proposal:

Like usual, Jo and I's minds immediately gravitated towards gaming, and we sought out inspiration from such vintage racing titles as Pole Position and Outrun. I also recalled an opening sequence from the SNES game Vegas Stakes that included a road trip sequence to Vegas. [1] Perfect.

Johannes begun development by laying out a real-time chat and radio experience using a combination of Backbone.js and the backend tech offered by our friends at Firebase, while I wrestled fitting all required elements into a clean and balanced front end layout. Once that was setup, we combined our efforts on the car animation which was built entirely with animated GIFs (more on that later) and CSS, creating an app that feels like Cruis'n World meets Turntable.fm.

Finally, we utilized the the open-graph meta data and media from our logged in Facebook users to randomly generate user specific signage such as an exit sign directing you to your hometown or a billboard using your Facebook photos. The end result is fucking rad, and one of our first users said it best:

“OMG. It's like, driving through my life.”

Indeed, Hotchick1987. Indeed.

All in all, it was incredibly fun build, and one that was extremely hard for us to stop working on. Hell, I just wanted to build Cruis'n World at the end. Don't believe me? Try punching in the Konami code. ;-)

Anyway, here's a few highlights from my side:

A Bolt of Typography

Bolt Typeface

I knew early on that I was going to be utilizing the bolt logo to accent several different areas of the design, so I needed an easy way to draw and color it it without making 1,000 different versions. One word: Webdings. Seriously.

Inspired by what Symbolset has been doing with their semantic symbol fonts (also used on this site), I decided to recreate the bolt as an OpenType font. This keeps the mark perfectly crisp on retina displays [2] and gives me the ability manipulate it with CSS functions like color and text-shadow.

Here's how I'm using it:

// Include it using @font-face
@font-face {
  font-family: "Battle Bolt";
  src: url("/battle-bolt.otf") format("opentype");
}

// Add it to a pseudo element so it shows up before each h1
h1:before {
  color: red;
  content: "!";
  font: 25px "Battle Bolt";
}

Paving the Road

We paved the road you see now three times before we were happy with the result. Our first attempt used CSS3 transforms to create the asphalt and stripes in perspective, influenced by this tutorial we found. However, that didn't seem to run well on non webkit browsers.

Johannes then had the bright idea to create the road as an animated GIF and tasked me to figure out the best way to do this. Luckily, I had been dabbling in Photoshop CS6's 3D functions recently, so I had a pretty good idea of how to pull it off. I started by creating a flat road texture:

Texture

Then I created a new Photoshop document with a single object layer. That layer was converted into a 3D Postcard which created a three dimensional plane I could move, rotate, and scale within the scene. I then applied the texture created earlier to the plane's diffuse property, making sure to scale it vertically for nice repeating. Finally, I used Photoshop's Timeline to animate the camera moving from a single stripe to another. Throw in some GIF repeating, a fog overlay, and you've got yourself a road:

GIF

Of course, I'm never satisfied, so I actually programmed the road once more using Three.js and WEBGL. The result was awesome, but what we gained in smoothness and features, we lost in browser compatibility. So I dropped my pursuit for perfection, in exchange for the animated GIF.

Closing Arguments

As mentioned earlier, we pulled in meta data and photos from Facebook in order to customize our signage experience. When used within the right context, this technique can be very powerful and sometimes downright nostalgic. However, you shouldn't perceive that randomly pulled photos will fit into any context. Linkin Park found that out the hard way in their latest interactive video Lost in The Echo.

The look of the video is typical Linkin Park: post apocalyptic world, sweaty actors, and an overall gloomy aesthetic. A small group of survivors find themselves in an abandoned warehouse where they are handed photos, presumably of significant others, which causes each of them to break down emotionally and you know… shatter into pieces. Prior to combusting, each person glances down at the photo they are holding and are shown a photo mined from your Facebook account. Now, don't get me wrong. What they've created is awesome. However, the outcome always looks something like this:

Lost In The Echo

Yes.

That is a watermelon jello shot in my hand. Definitely not worth crying or combusting over. Hilarious. However, to Linkin Park's defense, they get it.

Here's what happened when Mike Shinoda connected:

“I never put personal pictures up on it so… half the pictures were of dogs, landscapes, and random silly things,” he says. “It was hilarious to watch this video pull those pictures and see the characters in the video break down in tears over a picture of a ham sandwich.”

Touché, Mike. You win this round.

Tracking the Runaways

Johannes and I now plan to step back from artist campaigns for a bit to build some scalable software. If you'd like to hear more about that, make sure you subscribe here.


[1] I also stole their rad card flipping avatar design to show when users entered and exited the vehicle.

[2] I recently upgraded to the shiny new Mac Book Pro with retina display and while it is incredible, it also presented countless problems while developing these apps. Mostly because browsers / apps haven't really caught up to the technology.

 
33
Kudos

ATM?

ATM

A group of friends and I have been karaoking quite regularly at a particular dive in Hollywood's Koreatown. I won't give away the name because it's our dirty little secret and we basically run the place when we're over there. It's not located in the safest part of town so we tend to roll deep for safety. My rule is, “There must be more people in our party than patrons at the bar.” Luckily for us this particular spot only has about three regulars at any given time.

There's the guy with one (maybe two) teeth, who loves Frank Sinatra so I make it a point to lounge “Lady is a Tramp” once or twice a night for him. Then you have shy latino guy who always finds the courage to get on stage before the night is over. He also thanks you, very seriously, for your choice of Nickelback and Creed. Love that guy. Finally, you've got a diehard Social Distortion fan that has definitely had a few too many, considering how often the bartender threatens to bash his knuckles in if he won't stop screaming and banging on the bar during conversation. He tends to doze off around 9:30PM or so.

Oh, and speaking of that bartender: she's incredible. A tough korean lady who knows you're drinking Coors Light, calls you her son, and makes sure you're properly hydrated throughout the night… which may have something to do with the lack of air conditioning. No matter. There's an armada of tiny fans and if you sit at one particular corner (the one closest to the stage) it's actually quite refreshing. Did I say stage? I meant 4x4 foot curb that fits two comfortably (Leather & Lace) and four barely (Bohemian Rhapsody.)

Depending on what night you're there, you'll get one of two Karaoke DJs:

  1. K-Town Whitney Houston, who forgoes the two wired mics for her own bedazzled wireless one.

  2. And older Korean man who always starts the night off positive, but gets quite grumpy at the end as he adjusts the (never posted) fee per song. We tend to drop $20 in the bucket at the start just to keep him happy.

Every single time we go (and I mean every single time we go) we meet someone awesome. A wildcard. Some nights it's a Hollywood record producer taking a break from tracking. I'll call him Hollywood Condiment. Then there's that one guy who was obviously on a failing date and spent 50% of his time arguing with her and 50% dueting Little River Band with you. I wonder whatever happened to that guy…

Anyway, my reason for telling you all this was because an incredible occurrence happened there a few weeks ago. You see, this bar is cash only. It also happens to be right next door to a cash only non-stripping strip club called… well, I'll just say it probably influenced the Louis Vuitton Don. If for whatever reason, we find the urge to pop-in post Karaoke, we need to make sure we're carrying the appropriate amount of cash to, you know, pay the employees. If there's one thing this particular section of K-Town doesn't have, it's fucking ATMs. And besides, as mentioned earlier, you don't want to be walking around here when the sun goes down.

My buddy and I were discussing this fact at the bar and our conversation was overheard by the barback. He moseyed on over to our corner, and very politely said, “ATM?” This is the first time my buddy and I look over to each other and nod yes, “That would be nice, thanks.”

“Ok, this way.”

Maybe it was all that karaoking and Coors, but we're now following the barback to the rear parking lot or as we call it: the secret entrance. (You know, for VIPs and such.) I've been in this parking lot before. There ain't no ATM. So what's the deal?

“ATM?” “Yep.” “Ok, let's go,” he says, pointing to his vehicle.

Second glance…

“Okay.”

We are now in a stranger's vehicle bound for… well we're not sure exactly. It's at this point that we give each other the third glance and begin to second guess our decision. Right about the time we've exhausted every murder scenario in our heads, he pulls up to… the Bank of America. We jump out, make a withdrawal, and return to our chauffeured ATM-mobile back to the bar.

Maybe I was relieved that he didn't drop us at the LA river, or maybe it was because I knew I was going to be able to write this blog post, but I gave the guy a $20 tip. Ok… it was because the ATM only dispensed twenties… Regardless, we made it rain that night. While we have revisited the bar several times since then, we have yet to utilize the ATM again. Though I don't think we would hesitate. You gotta be bold.

 
84
Kudos

In Rock We Trust

$100 FOO

Minting $1,500,000 at the Federal Reserve of Foo Fighters

A little over a week ago, the Foo Fighters played both Reading and Leeds festivals in the UK. Each of these shows ended in a confetti cannon launch of several hundred thousand fake FOO_$100 bills. On the backside of each of these bills we subtly scrawled the web address inrockwetrust.co.uk. I was tasked with building an experience that carried this theme onto the web at that URL.

What made this project really fun was how seamlessly we found ourselves (and our fans) going back and forth from real life to the web and it's a perfect example of just how connected these two spaces can be. So before we talk about the Internet, let's take a look at what went into the real world event, as it was literally launched into fans' faces.

Is this the Real Life?

Master Die

The original bill was designed by Jason and Doug over at Morning Breath whom you may know from Wasting Light, Era Vulgaris, and Slayer. The final version is fucking rad and management was able to add a ton of hidden messages and band references in addition to the inrockwetrust.co.uk URL.

On a personal note, I just hired Morning Breath to design something for me, but you'll have to wait and see what that's all about.

Raining Money

Foo Fighters at Leeds

Foo Fighters coordinated with the SFX geniuses over at Quantum Special Effects to turn the confetti launch into a reality, employing their entire arsenal of 24 stadium shot cannons. Then on August 24th, at the end of the first set and despite rain, the band launched a shit ton of fake Foo Fighters dollar bills.

Fans in the front were lucky enough to grab some and photos like this started trickling onto the web in no time. It didn't take them long to decipher the secret URL on the back and head over to inrockwetrust.co.uk.

But what was waiting for them when they got there?

Is this just Internet?

Having just come off the Bloc Party Four campaign, where I did a lot of server side image manipulation, I thought it would be a good idea to allow fans to strike their best presidential pose and become part of the FOO bill. Initially I setup an app similar to Four, in which your Facebook profile pic was pulled in automatically and placed inside the bill. This automation made it difficult to frame the face appropriately, and forced me to make two very important decisions:

Anonymous? Don't you want those emails, dude?

My thinking was that we were already adding a barrier of entry by requiring a webcam, and removing any login requirements should make up for this.

Ok, but what did the band get out of this?

Look, you don't always have to sell something. Try being cool for the sake of being cool every now and then. You'd be surprised how well that works out in the long run.

Now then, let's talk programming.

Webcam Portrait Gallery

Webcam

Utilizing a user's webcam is pretty straightforward these days, but talking to different webcam types within different environments while also using the best available technology can be a little tricky. That's where getUserMedia.js comes in. Written by Addy Osmani, this library exists as a shim that calls on WebRTC for Chrome, Opera 12, and Firefox nightlies. All other environments fallback to the also excellent flash powered jQuery-webcam plugin, written by Robert Eisele.

Rather than explaining that setup here, I'd suggest checking out Addy's well documented demo app. I would, however, like to share how I was able to get the webcam image over to my server (from both webrtc and flash) for manipulation as that was a bit of a learning experience. Both methods consist of putting the captured image onto a temporary canvas and getting a Base64 representation of that image which is then sent over to the server.

For WebRTC, it couldn't be easier:

// Get the HTML5 video element
video = document.getElementsByTagName('video')[0];

// Draw that element onto the canvas
App.context.drawImage(video, 0, 0, App.options.width,    App.options.height);

// Use toDataURL to get your Base64 representation
App.canvas.toDataURL('image/png');

Flash, that son of a bitch, makes it a little harder for us:

// Split up the image data into columns
col = data.split(;);

// Declare some variables, img comes from a getImageData() called in `init`
img = App.image;
w   = 320;
h   = 240;

// Loop through each of the image columns and place into canvas image
for (i = 0; i <= 319; i += 1) {
  tmp = parseInt(col[i]);
  img.data[App.pos + 0] = (tmp >> 16) & 0xff;
  img.data[App.pos + 1] = (tmp >> 8) & 0xff;
  img.data[App.pos + 2] = tmp & 0xff;
  img.data[App.pos + 3] = 0xff;
  App.pos += 4;
}

// Once completed, you can put it onto our temporary canvas and use toDataURL
if (App.pos >= 4 * w * h) {
  App.context.putImageData(img, 0, 0);
  App.canvas.toDataURL('image/png');
}

On the server side I'm running a very basic Sinatra app that accepts the image, temporarily stores it as a StringIO, and saves it to my S3 using Carrierwave.

image = params[:image].gsub("data:image/png;base64,", "")

io = FilelessIO.new(Base64.decode64(image))
io.original_filename = "bill.png"
io.content_type      = "image/png"

bill = Bill.create(name: params[:name])
bill.image = io
bill.save

In doing so the image passes through all of my Carrierwave processors and gets minted accordingly:

Minting the Money

Minting $100 FOO

The first step in minting is to use MiniMagick to turn the full color webcam photo grayscale and then give it a nice money green tint.

img.combine_options do |c|
  c.colorspace 'Gray'
  c.fill '#dfdfc8'
  c.tint '100'
end

Morning Breath was nice enough to provide me with the layered Photoshop source file for the bill, which I used as a guide for setting up my composition functions. In addition to the bill image, three different textures were overlaid on top using the Multiply composition method. This allowed the textures to bleed through and leave a nice big crease across the users face.

img = img.composite(texture) do |c|
  c.compose "Multiply"
  c.gravity "NorthWest"
end

One thing you'll notice on the front of the original bill is the name “Claire” is written in pen. Now I won't tell you who that is, but I will tell you how I mimicked this on my version, allowing the user to provide their own name and writing it on the bill.

First, we setup a nice handwritten typeface by passing in the TTF file path manually. Then we give the type a nice pen color and size it up for writing. Finally, we draw the provided name over the user's face.

img.combine_options do |c|
  c.gravity 'NorthWest'
  c.font 'public/learning.ttf'
  c.fill "rgb(73,70,130)"
  c.pointsize '60'
  c.draw "text 135,70 '#{ model.name }'"
end

Raining Money II

While it isn't as exciting as shooting off 24 stadium cannons… the CSS3 animation used to mimic a falling bill is actually quite awesome, and the live feed of data had the benefit of showing me errors… literally falling in front my face. Anyway… all it took was a read through of this guide and a few keyframe animations:

/* Fall from the top to bottom */
@keyframes fall {
  0% { top: -200px; }
  100% { top: 1000px; }
}

/* Flip and rotate the front */
@keyframes front {
  from { transform: rotate(-50deg) rotateX(0deg); }
  to { transform: rotate(50deg) rotateX(360deg); }
}

/* Flip and rotate the back */
@keyframes back {
  from { transform: rotate(-50deg) rotateX(-180deg); }
  to { transform: rotate(50deg) rotateX(180deg); }
}

Is this the Real Life?

What? That's not how that Queen song goes.

I know.

However, there was one more idea that didn't make it into the final campaign that brought us back to real life once again. You see, I wrote a piece of code that would actually print the bills on a printer I controlled as they were being generated by users. In the end, it seemed like a big waste of paper and would quickly get out of control given the velocity of minted bills.

I plan on revisiting this technique in a future blog post about building your own Instagram printer. If you're interested in that, I'd suggest subscribing to my newsletter.

As always, thanks for reading, and please let me know if you have any questions or comments via Email or Twitter.

 
82
Kudos

Isotoping Sound City

Last week I was tasked by Mr. Grohl to build a real website for his upcoming Sound City documentary. And by real I mean: a place where a synopsis, cast, trailer, and other pieces of media can live and be discovered… not an FM radio or console fader.

Boo. Okay, DG.

We also needed a place to release a new series of videos in which Dave asks everyone that took part in the movie the same question: What was your first music memory? Watch this one with Kevin Cronin to see what I'm talking about. Tom Petty has the best story, but you'll have to wait for that. ;-)

Making Magic with Isotope

Jason Kadlec of CHNL told me about this fancy jQuery layout plugin called Isotope a few weeks ago and I've been dying to try it. Isotope describes itself as “an exquisite jQuery plugin for magical layouts.” Magical indeed. Check out some of the ridiculous demos here.

Setting up Isotope is as easy as adding a container of items to your site and calling isotope via jQuery on that container, providing the class of the items within:

$('#isotope').isotope({
  itemSelector: '.item'
});

Isotope sniffs out a default column width from your first item, but you're better off defining this yourself within your CSS. I chose to use one column width for all my items, but Isotope is smart enough to understand a dynamically changing width. Just remember, padding counts towards your total width.

Isotope also has an insanely smart filtering engine powered by simply adding classes to your items. For example, here's how I would define Tom Petty's “Musical Memory” video:

<div class="item video musical-memory tom-petty">
  <!-- Video content here -->
</div>

I could then easily create buttons on my page that sort the Isotope to each particular class. [1] In my case, allowing visitors to filter by content type, category, and even cast member. [2] So easy, so awesome.

Special thanks to Jason for the tip and David DeSandro for writing the plugin!

Balancing the Design

Neve Console

The Isotope container is a heavy piece of real estate and you'll need to balance any other elements by adding a bit of weight to them. I did this by adding a thick border to the left side of my sidebar. I feel like this allows the sidebar to live next to the Isotope without feeling out of place. Alternately, you could try designing a lighter Isotope layout, void of any thick paddings or heavy colors.

Aesthetically, I've brought in some of the elements I've been setting up throughout the campaign such as the dark theme and “tape” textures taken from the actual Sound City fader. [3] I think these simple guidelines lend themselves well to the actual content being featured on the Isotope.

Generating the Content

Great… but, where does the actual content come from?

Well, you could manually add the items to a static page as needed, but that's just dumb. Instead, you'll want to to dynamically update the Isotope with a database of content and the categories they belong to.

I considered developing my own own for about two seconds before I realized the Tumblr API was built for this shit. In addition to providing all descriptions, tags, and associated media, Tumblr also distinguishes between several post types: text, photo, quote, link, chat, audio, and video. This makes their infrastructure perfect for Isotope. So, after creating a few ICanHaz.JS templates for each content piece:

<script id="video" type="text/html">
  <div class="item video">
    <a href="{{ permalink_url }}" target="_blank">
      <img src="{{ thumbnail_url }}" />
    </a>
  </div>
</script>

I was able to connect a simple Tumblr API call to a case loop that generated HTML from each associated post type template, which was then appended to the Isotope.

$.getJSON("http://api.tumblr.com/v2/blog/TUMBLR_URL/posts?api_key=KEY", function (data) {
  posts = data.response.posts;

  for (i = 0, len = posts.length; i < len; i++) {
    switch (post.type) {
      case "quote":
        html = ich.quote(post);
        break;
      case "video":
        html = ich.video(post);
        break;
    };

    $('#isotope').append(html).isotope('insert', html);
  }
});

So why not just use Tumblr altogether?

Honestly, I hate their theming engine. However, I'm thinking that once this thing is perfect, I could possibly write a script that generates a Tumblr theme from the provided layout and templates. That could be rad.

Let's get Real_time_, Son

After digging through Tumblr's poorly documented real-time API, I finally found a solution that would allow me to dynamically add items to the Isotope as they were posted to Tumblr. I used a combination of Superfeedr and Pusher to do this.

First, we need to configure both Node package modules on our server:

// Configure Pusher
pusher = new Pusher({
  appId  : APP_ID,
  key    : KEY,
  secret : SECRET
});

// Configure Superfeedr
superfeedr = new Superfeedr(USERNAME, PASSWORD);

Then we can connect both:

superfeedr.on 'connected', function () {
  // subscribe to the sound city feed
  superfeedr.subscribe('http://TUMBLR_URL/rss', function (err, feed) {
    // subscribed ...
  }),

  // on each notification...
  superfeedr.on('notification', function (notification) {
    // trigger an action of tumblr on the soundcity channel
    pusher.trigger("soundcity", "tumblr");
  })
};

And finally, back on the client, we'll user Pusher's JavaScript library to listen for Tumblr activity. For each ping we get, we should ask Tumblr for the newest post and add it to the Isotope.

// Configure Pusher
pusher  = new Pusher(APP_ID);
channel = pusher.subscribe('soundcity');

// Listen for new Tumblr post...
channel.bind('tumblr'), function (data) {
  // Get the newest post from Tumblr and add it to Isotope
});

So now, when I take a Sound City photo with Instagram and make sure Tumblr is connected, once posted, the photo will automatically show up on the front page of Sound City. Probably unnecessary, but bad ass, and surely you'll grab that visitor's attention when the layout goes all Minority Report on their ass.

Looking Ahead

The current setup works, but it's far from perfect. I plan on redeveloping the entire client side infrastructure with Backbone.JS in anticipation for adding one-click media streaming to the video and audio items. Backbone will also help simplify the template rendering process.

Sign-up if you'd like to hear more on that in the future!


[1] Using jQuery BBQ, you can create a bookmark-able hashed URL history for each filter. Instructions here.

[2] Since the Sound City documentary has so many participants, I thought it would be neat to auto filter the site to a particular cast member depending on where referral traffic comes from. In other words, if you clicked a link to the site on tompetty.com, you would see Tom Petty content near the top. This is probably a bad idea, but worth some A/B testing regardless

[3] Shoutout to Flood Standard for being the perfect typeface for highlighter tape writing.

 
123
Kudos

Bloc Party Four

FOUR

Four is the magic number

Bloc Party are back from hiatus [1] and are preparing to release their fourth record, appropriately titled “Four,” next week on August 20th. In addition to this being the band's fourth release, it also represents them getting back to their musical roots, as it was only the four of them in the studio. Listen to the lead single “Octopus” to hear what I mean.

This theme of simplicity is carried onto how the record is represented visually as well. The cover displays four olympic colored rings on a black background. Within the linear notes you'll find four similarly colored images of each band member, leaving us to believe that the rings must represent each of the members.

Oh, and a quick aside while I'm on the topic of visuals: both the band logo and linear notes are set in default web fonts! I guess their logo was always set in “Gill Sans,” but the italicized “Georgia” within really takes the cake. This band was built for the web.

So… the number “Four,” simplicity, colored circles, hued band photos, and default web typography… how could I turn these parameters into an interactive and accessible album premiere? And, how could users become part of the theme themselves while simultaneously increasing the viral reach?

Idea

What if while in the premiere, users were able to personalize their “Four” experience by grouping themselves and three of their closest friends into their own four? And what if this personalized version was sent to those friends in the form of a dynamically generated composite photo and post?

Four Post

I think this story is much more meaningful than a “retweet” or “like” AND a lot more fun. People like to click on stories about themselves, and if you perhaps included a tagged colorized image of that friend in the post… oh, they'll click it alright.

Goals

In addition to the interactive twist, this app also needed to cover several basic goals and features:

I knew early on that this was going to be too much content to jam into a single page's height and width, especially when thinking about iOS devices, so I decided to design a longer layout than usual.

Layout

Kitty and Notes

After spending some time sketching out several different layouts, I found the answer right in the theme and divided the site into four sections:

Given the length of layout, now four times longer than what I typically build, I wanted to include an interesting way to navigate between these sections. I found a jQuery plugin called Curtain.JS written by Victor Coulon that animated between site sections by using a curtain-like reveal method. For example, the intro section would pull up, like a curtain, to uncover the Interactive section. Pretty rad.

Finally, I established a central column of content around 300 pixels wide in anticipation for a responsive mobile layout. Mobile First!

UX

I love designing within the constraints of minimal assets, because you can always find interesting ways to stretch things out. In fact, I find it much more rewarding to expand on an album's aesthetic vision rather than complying one-to-one with the base assets.

I started by using the four band member photos to establish background textures for each section by desaturating, blurring, and brightening || darkening them depending on where they fell within the layout. This gave me a nice base to focus on more clean typographic design for the majority of the site. Did someone say Georgia italic?

I used a combination of SASS and Compass to establish some sophisticated cross-browser mixins that could generate both the ringed logo circles and user avatar circles. Check out this logo example:

@mixin center($size)
  left: $size
  top: $size

@mixin square($size)
  height: $size
  width: $size

@mixin circles($size, #border, $spacing, $color)
  @include inline-block()
  @include square($size)
  position: relative

  li
    @include border-radius(100%)
    background-position: center center
    background-size: cover
    border: $border solid $color
    position: absolute 

  @for $i from 0 through 5
    li:nth-child(#{ $i + 1 })
      @include center($spacing * $i / 2)
      @include square($size - $spacing * $i)

All the social and player buttons were created using Symbolset's excellent hybrid typeface meets iconography meets OpenType semantic symbol font. Just go fucking buy it already.

Four Editor Idea

By far, the most difficult UI piece to figure out was the Four editor. I actually printed out paper templates that only contained the ringed circles in the middle of a stark page, and didn't allow myself to take a break until I was onto something. Which is probably not the best idea… but I was lucky enough to have a major breakthrough about 8 hours into that session.

It originally included a way for users to select a unique color for each member of their four. I ended up dropping the color picker to stick with the album's color choices, simplify the dev, and lower the barrier of creation. Also, my buddy Johannes said, “Users are bad at picking four colors that look nice together.” :-D

Client

This project served as a proper renewal of my love and understanding for the model-view-collection goodness that is Backbone.JS. I was able to setup a a few models and collections for both the player and editor that effectively powered the entire user experience, client side, from a single page. And thanks to Backbone, all views associated with these models change automatically when they get created, updated, or destroyed.

Take a four Member model for example. Upon accepting the Facebook ID of a particular friend from the select box, the app creates a new Member model using the given user data:

Member.create({uid: 124, name: "Kele Okereke", position: 2});

This single line of code transparently posts to a member creation action on my server that responds with a success or failure. Upon success, Backbone initializes the newly created member and creates the appropriate view for it. It also checks to see if you've created four total members and if so, flips the view from editing to publish mode. Abracadabra!

Another big component of the client was Facebook. And while I've tried to keep up with the numerous changes to their developer platform over the years, I still went into this idea basically knowing zero. But alas, the days of server-side only authentication are over, and the developer docs have a nice overview of of the JavaScript SDK in action.

Authenticating a user and pulling a friends list is pretty straight forward, but what about posting a dynamically generated photo story with four tagged friends? Challenge accepted:

FB.api('/me/photos', 'post', {
  message: "Here's my FOUR. Who's in yours? http://four.blocparty.com",
  url: PHOTO_URL,
  tags: [
    { tag_uid: Members[0].get('uid'), x: 25, y: 25 },
    { tag_uid: Members[1].get('uid'), x: 75, y: 25 },
    { tag_uid: Members[2].get('uid'), x: 25, y: 75 },
    { tag_uid: Members[3].get('uid'), x: 75, y: 75 }
  ]
}, function(response) {
  // posted!
});

So, apart from a few cross-domain issues, developing with Facebook's JavaScript SDK was actually quite fun. 7/10 would dev again.

Server

I set my hipster stack aside for a second in exchange for a rack based Sinatra application, because I had a few Ruby gems in mind when conceptualizing this and I needed a quick and easy database infrastructure for storing users and members. I chose the well documented ORM, DataMapper, to create a User model and a many-to-many model called Memberships. Every user had X amount of member users through this group membership model. Or in code speak:

class User
  has n, :memberships, :child_key => [ :source_id ], :order => [ :position.asc ]
  has n, :members, self, :through => :memberships, :via => :target, :order => [ :id.asc ]
end

class Membership
  belongs_to :source, 'User', :key => true
  belongs_to :target, 'User', :key => true
end

I then wrote a light-weight API to handle the creation, deletion, and updating of members when communicated with the client. In other words, CRUD! Each of these actions returned a JSON representation of the model in question.

As for those dynamically generated images… :-)

I've been wanting to use this trick for some time now as I have an unhealthy obsession [2] with the idea of using a server to process things like video, audio, and yes, images. This was actually a pretty basic example of this technique, and all it took was the Ruby gem MiniMagick to make it happen. Let's look at an example of coloring a friends photo once they're selected:

def colorize
  manipulate! do |img|
    img.combine_options do |c|
      c.colorspace 'Gray'
      c.fill '#C32025'
      c.tint '100'
    end

    img = yield(img) if block_given?
    img
  end
end

Magick? You fucking bet. This was translated into a CarrierWave post processor that was called simply by supplying the remote URL to a friend's Facebook profile photo. You know what else rules about CarrierWave? S3 support. So, once configured, all I needed to do was this:

member.remote_photo_url = "http://graph.facebook.com/#{ UID }/picture"

And this happened:

  1. The photo was grabbed from the remote Facebook URL and cached locally.
  2. It was then resized, cropped, grayscaled, and tinted according to a dynamically supplied color.
  3. Finally, the photo was saved to my S3 bucket and stored as a CarrierWave file representation in my local database.

Seriously. Now if that doesn't give you a nerd boner, I honestly don't know what would. You have one? Good. You're welcome.

Thanks

I hope this gives you a nice overview of what goes into an intense two week project from concept to reality. But you don't have to take my word for it… go experience the site and listen to the new record. It's damn good. “Kettling” is my jam!

As always, thanks for listening. If you have any questions or comments, feel free to email or tweet me. And if you can't get enough of this sort of stuff, subscribe to my email list. I promise not to spam you.


[1] The band was on hiatus from 2009 to 2011.

[2] Johannes and I wrote a service called Waveform.js to convert SoundCloud waveform images in an array of floating points.

 
93
Kudos