Philip Bulley

Posts

September 25, 02:37 PM

Until a few days ago, if you’d wanted to use a web font (@font-face) hosted on AWS S3 within a document on another domain (ie. an EC2 instance) – unless you were happy with the near-crazy Base64 embedding into bloated CSS file technique – you’d be out of luck when it came to the goodie-two-shoes of web browsers, Firefox.

Firefox would demand that you specify an “Access-Control-Allow-Origin” header on your server’s response. This is not rocket science when it comes to serving files from a regular web server such as Apache, but of course S3 is very different to all of the other kids (with good reason). It was just a shame that S3 didn’t have a way of specifying such a header bucket-wide, until now.

Via the AWS S3 console:

go to “Properties > Permissions > Edit CORS Configuration” and paste the following XML:

1
2
3
4
5
6
7
<CORSConfiguration>
 <CORSRule>
   <AllowedOrigin>*</AllowedOrigin>
   <AllowedMethod>GET</AllowedMethod>
   <MaxAgeSeconds>3600</MaxAgeSeconds>
 </CORSRule>
</CORSConfiguration>

This Cross Origin Resource Sharing policy allows GET requests from any domain. The response will be cached by the browser for an hour, preventing multiple unnecessary preflight requests on your S3 bucket should users be repeatedly loading style sheets containing @font-face rules across HTML pages.

Official documentation here.

November 06, 06:56 AM

My first project at Stinkdigital was also the most technically challenging I’ve ever been briefed on to date. Essentially, the brief was to create a full-screen audio/video mixer featuring approximately 50 audio channels and two HD quality video streams, all of which must be synchronised. If that wasn’t enough, each audio channel should also simultaneously display its current pitch and when clicked on should display it’s individual musical score in it’s entirity. Oh, and to avoid the use of a streaming server if possible!

Visit interactive experience website

I came out of that briefing simply thinking, “wow!”. The complexity that the project offered was certainly a little daunting. “Can Flash Player step up to the task?”, “Is something like this even possible at this scale?” were a couple of the thoughts that went through my mind. Without truly knowing the answer to those questions yet, my confidence levels increased upon hearing that the production team had already scheduled at least 2 weeks for me to create a prototype (prototype! awesome! this can be done! you guys rock!).

Multi-track audio synchronization

Immediately it was obvious that Flash Player had some shortcomings when it came to synchronizing multiple sound channels with beat-perfect accuracy. The “workaround” was nominating a master track and evaluating it’s time against the time of other/slave channels, if the sync went out by a specified threshold, I’d stop the slave channel and restart it at the master time. Not great, but it did the job.

I wanted to make switching between the tracks of the individual musicians as smooth and as quick as possible. Without resorting to the use of a streaming server, the idea was to cut up each track into 8 separate MP3 files. The first MP3 file would contain the full duration of the musician’s solo performance, the second would begin at one eighth into the performance and so on until the last MP3 which would contain the final eighth. That way, when the user decided to switch to another track, we’d look at the current master duration then figure out which MP3 to load. This solution is great, as it utilizes progressive download and is CDN cache friendly. The only downside is having to create and manage the 440 MP3 files!

Multi-track video synchronization

Video would of course, suffer from the same sync issues as audio. But unfortunately due to the way keyframes work, the same solution was not an option as you cannot seek to individual frames (unless you were to encode the video with a keyframe on every frame, inflating the file size to ridiculous levels). Instead the best solution did at first seem a little crazy, but it worked a treat; combining both camera angles in one video stream would ensure that synchronization was 100% perfect.

Example of 2-up video stream running in VLC Player

Encoding the video would be a challenge, squeezing that much video down the pipe at any one time would need a considerably high bitrate if it were to look half decent. We managed to get good results with 2.4Mbps for 2x480p and 3.4Mbps for 2x720p using the Sorenson H.264 encoder (the Main Concept H.264 encoder had tighter restrictions on video dimensions). Such a bitrate would mean that we’d have to buffer a large amount of video before starting playback – a great excuse to use some of the wonderful time lapse photography of the Metropole Orchestra setting up their instruments that had been supplied to us.

Metropole Orchestra timelapse photography sequence as used in video buffering preloader

Real-time pitch display and musical score

With a little bit of help from Melodyne and this online MIDI to XML converter, creative technologist Dan Beattie did a great job of figuring out a workflow to turn solo MP3 tracks of each artist into something that could be used within Actionscript. Fellow developer Jasper Stocker then did a sterling job of parsing that XML to power the musical score display.

A labour of love

That’s exactly what this project became, thanks to Stinkdigital allowing myself and Flash Dev Neil Nand the time to really add the finesse in terms of animation and functionality. It turns out that it was all worth it too, so far it’s scooped awards at Cannes, Clio, D&AD, SXSW, One Show, plus others (they went and listed them, cute little icons included, on the Stinkdigital site).

(Via Stinkdigital London and Tribal DDB Amsterdam)

August 01, 04:39 PM

I have to admit, that this was a task I wanted to accomplish quickly. The kind of ‘quickly’ where you just do a Google search and nice internet person tells you which APIs to use. But no such internet person seemed to exist, so I figured I’ll share my solution.


My problem was that I have a disk-based Codeigniter DB cache running on several EC2 instances. But when I make a change to my RDS database, I need to clear each cache on each individual running EC2 instance. These EC2 instances are within an auto-scaling group, so there’s no guarantee of how many caches will need to be cleared when I decide to purge.

The basic strategy involves calling (using the AWS PHP SDK):

  1. AmazonELB->describe_load_balancers() passing the name of your load balancer
  2. AmazonEC2->describe_instances() and parsing out the dnsName

Here’s a quick Codeigniter model that covers querying my live load balancer and then EC2 to get the public DNS names that I can later use to construct a URL to each running instance:

/models/aws_model.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<?php

require_once APPPATH . "/models/abstract_model.php";
require_once APPPATH . "/models/vo/AWSEC2InstanceVO.php";
require_once "application/libraries/aws_sdk/sdk.class.php";

class AWS_model extends Abstract_model
{
    function __construct()
    {
        parent::__construct();
    }

    /**
     * Query AWS to get a list of AWSEC2InstanceVO
     * for each instance in the ek_live load balancer group
     *
     * @return array
     */

    public function getLiveAppInstanceList()
    {
        return $this->getInstances( $this->getLiveAppInstanceIDList() );
    }

    /**
     * Gets the instance IDs currently running under the live
     * load balancer
     *
     * @return array
     */

    public function getLiveAppInstanceIDList()
    {
        return $this->getELBInstanceIDList( 'ADD_YOUR_LIVE_LOAD_BALANCER_NAME_HERE' );
    }

    /**
     * Query AWS to get the list of DNS names for specified instances.
     * Use getLiveAppDNSNameList() to get for all live instances
     *
     * @param $instanceIDList   array   An array of instance IDs
     * @return array
     */

    public function getInstances( $instanceIDList )
    {
        $ec2 = new AmazonEC2();
        $response = $ec2->describe_instances( array( 'InstanceId' => $instanceIDList ) );
        $instances = $response->body->reservationSet;

        $instanceList = array();
        foreach( $instances->item as $instance )
        {
            $item = $instance->instancesSet->item;
            $vo                     = new AWSEC2InstanceVO();
            $vo->id                 = (string)$item->instanceId;
            $vo->imageID            = (string)$item->imageId;
            $vo->state              = (string)$item->instanceState->name;
            $vo->stateCode          = (int)$item->instanceState->code;
            $vo->privateDnsName     = (string)$item->privateDnsName;
            $vo->dnsName            = (string)$item->dnsName;
            $vo->type               = (string)$item->instanceType;
            $vo->launchTime         = (string)$item->launchTime;
            $vo->availabilityZone   = (string)$item->placement->availabilityZone;
            $vo->privateIpAddress   = (string)$item->privateIpAddress;
            $vo->ipAddress          = (string)$item->ipAddress;
            $instanceList[] = $vo;
        }

//      echo "<pre>";
//      print_r( $instances );
//      echo "</pre>";

        return $instanceList;
    }

    /**
     * Gets a list of instance IDs under the specified load balancer
     *
     * @param $loadBalancerName     string      The name of the load balancer
     * @return array
     */

    public function getELBInstanceIDList( $loadBalancerName )
    {
        $elb = new AmazonELB();

        $response = $elb->describe_load_balancers( array( 'LoadBalancerNames' => $loadBalancerName ) );
        $instances = $response->body->DescribeLoadBalancersResult->LoadBalancerDescriptions->Instances();

        $instanceIDList = array();
        foreach( $instances[0] as $instanceID )
            $instanceIDList[] = (string)$instanceID->InstanceId;
       
//      echo "<pre>";
//      print_r( $instanceIDList );
//      echo "</pre>";

        return $instanceIDList;
    }

}

/models/vo/AWSEC2InstanceVO.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
 
class AWSEC2InstanceVO
{
    /** @var string */
    var $id;

    /** @var string */
    var $imageID;

    /** @var string */
    var $state;

    /** @var int */
    var $stateCode;

    /** @var string */
    var $privateDnsName;

    /** @var string */
    var $dnsName;

    /** @var string */
    var $type;

    /** @var string */
    var $launchTime;

    /** @var string */
    var $availabilityZone;

    /** @var string */
    var $privateIpAddress;

    /** @var string */
    var $ipAddress;

}

I can now call a URL/script on each EC2 instance like so:

1
2
3
4
5
6
7
8
9
10
11
12
$instanceList = $this->AWS_model->getLiveAppInstanceList();

$message = "<ol>";
/** @var $instance AWSEC2InstanceVO */
foreach( $instanceList as $instance )
{
    $url = 'http://' . $instance->dnsName . '/path/to/script';
    $result = file_get_contents( $url );
    $message .= "<li>".$instance->id." - ".$instance->dnsName."<ul><li>Result: <strong>".$result."</strong></li></ul></li>";
}
$message .= "</ol>";
echo $message;

Hope this saves you a bit of time :)

December 23, 11:11 AM

I’ve been using Alessandro Crugnola’s FlashTracer Firefox Add-on for years, and have become pretty accustomed to it’s neat vertical sidebar integration. I wasn’t particularly happy to see that the familiar interface had (subtly) changed when he introduced FBTracer aka FlashTracer for Firebug (pretty sure that’s just me being fussy though!).

The original FlashTracer is apparently only compatible up to Firefox 3.x, so to get it to work with Firefox 4+, simply do the following:

  1. Download the .xpi
  2. Unzip it (it’s just a .zip file with a .xpi extension)
  3. Open install.rdf and change em:maxVersion=”3.9″ to em:maxVersion=”20″ – this will keep Firefox from complaining up until the release of Firefox 20 (which at this rate, we’re probably only a couple of weeks away from!)
  4. You may want to increase the version number to 2.4 (em:version=”2.4″), to prevent confusion
  5. Zip all of the files back up, change the file extension to .xpi
  6. Drag the .xpi file onto Firefox 4+, and it should install

I’ve installed it with Firefox 9.1 and the only incompatibility (that I’ve noticed so far) is that you can’t change the font from the default serif. Not bothered :)

Old skool FlashTracer running perfectly well in Firefox 9

By the way, this minor hack works for making any Firefox Add-on compatible with a more recent version of Firefox. But of course, if the actual Add-on source code itself is not compatible with a recent Firefox version, it might crash, mess up your Firefox profile, brick your computer, automatically slaughter all wild foxes in your local area, etc, which of course, I couldn’t care less about I’m not responsible for :)

November 03, 02:40 PM

Diesel Island FW11 is an interactive video showcase of the clothing Diesel would like you to be wearing as the year draws to a close.

It’s predominantly an HTML5 video powered site, but seeing as the awesome <video> tag isn’t supported by some not-so-awesome browsers (looking at you IE8), the wide web world turns to the much hated Flash Player to save the day! Apparently only 61.75%* of internet users will be able to experience the HTML5 Video version, so there’s no getting rid of Flash just yet, even for sites such as this, that would be more suited to HTML5.

On the whole, this was a fairly quick job: creating a custom video player that would interact with the CSS/HTML/Javascript overlays.

Kudos to @ihart and @jjenzz for painstakingly crafting the HTML5 version to work across browsers including Safari on iPad. Via Stinkdigital and Santo London.

* Source: caniuse.com, September 2011.

November 16, 02:31 AM

Kino for Hugo Just Different is all about seeing things differently. An interactive film directed by Marco Brambilla allows people to explore five different scenes through the shades of Theatrical, Performance and Story, simply by tilting their heads from side to side. The webcam monitors the tilt position of the head, which in turn invokes seamless transitions from one intricately gorgeous environment to another.

My role on this project was to lead the Flash Development from the prototyping stage through to delivery. Various prototypes were created whilst exploring how best to implement the webcam head tracking:

  • OpenCV Marilena – This wasn’t used as it wasn’t as performant as I’d hoped, but mostly because it is only good at face detection when the face is in the usual upright orientation. Tilt the head and OpenCV will lose you. Maybe this could have been solved by writing a lower-level Haar Cascade descriptor file (the fontal face descriptor file alone is 26,161 lines of XML!), but before even contemplating digging that deep, it was definitely worth exploring a couple of other techniques.
  • Motion detection – Based on frame differencing and blob detection, this would let Flash Player know if the user has moved their head and where to. Not a bad method, but we found that this really required people to move their head from one side (of the camera’s image) to the other, all too much work for the user. We really wanted something a little simpler, ie. the relatively low in effort action of the tilting the head.
  • Frame Matching – this is the process of caching three calibration images, and comparing the current webcam input with them. Using the threshold frame differencing technique, it was possible to work out quite accurately which of the three calibration images the webcam input most closely matched. This worked really well and was what made it into the final cut.

The other challenge came in the form of video synchronisation, something Flash Player is not good at. Simply creating three instances of NetStream and starting playback at the same time will eventually result in time shifting between the videos. The solution was to use a single MP4 file stacked vertically with each video frame along with DisplayObject.scrollRect to define the visible area. Three videos each 960×540 would result in one video of 960×1620. It’s worth noting that the MainConcept H.264 encoder has greater restrictions when it comes to encoding videos of non standard dimensions. In which case the Sorenson H.264 encoder provides more flexibility.

Three videos stacked vertically in a single MP4 file

Thanks to Stinkdigital (via Blast Radius) for getting me on board such an awesome project :)

September 25, 01:06 PM

Everyone’s favourite Meerkat, Aleksandr Orlov, is back and this time he’s with a bunch of his friends – the whole village of Meerkovo, in fact. Meerkovo.com allows fans to engage and interact with one of the richest back stories in recent UK advertising.

Aleksandr welcomes you to his Mansion. Feel free to explore all of his meerkat oddities via the comfort of your own komputamabob. Simples!

My main role on the project was to lead the code architecture and build the shell, whilst ensuring that the work of the other Flash Developers would come together seamlessly. And as per usual, all of this under a very, very tight phase one deadline. Stay tuned as subsequent phases on this long-term project will see the addition of new shops and games.

Navigate your way through the village of Meerkovo via the map (zoom in and be sure not to miss the quirky detail...



Fans would be driven to the website via this supporting TV Commercial.

"There is daaark cloud over Meerkovo!"

The website work was carried out at The Mill (in conjunction with VCCP), already famed for their high-end broadcast VFX skills, they’re now venturing into the world of digital.

November 12, 06:57 AM

There were a lot of learnings in building CrossTweet for Android. Here are a few tips I’d like to share with you.

Maintaining a Single Unified Codebase

With the help of conditional compilation, I’ve managed to keep a single unified codebase with separate ANT deployments. One example of where conditional compilation came in useful was when providing functionality to close the mobile app, something you wouldn’t do with the web app running in Flash Player.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Stop the compiler choking on NativeApplication when compiling for web using playerglobal.swc
CONFIG::ANDROID
import flash.desktop.NativeApplication;

private function close( event:ErrorViewEvent ):void
{
    CONFIG::ANDROID
    {
        // Only do this if compiling using AIR for Android using (airglobal.swc)
        NativeApplication.nativeApplication.exit();
    }
}

// Don't forget to set the value of CONFIG::ANDROID when compiling, see the ANT build.xml

Another example was when forcing the mobile app to automatically go fullscreen on startup, something which would produce errors if attempted within Flash Player.

1
if(CONFIG::ANDROID) stage.displayState = StageDisplayState.FULL_SCREEN;

Of course, keeping a entirely unified codebase can have it’s advantages, but may not be the best approach for every web & mobile app pairing. On a larger project, I’d probably lean towards unified models and controllers, but with separate views, something easily achieved with the dependency injection that a framework like Robotlegs provides.



Application Manifest

If you’re familiar with building desktop AIR applications, you will have created an AIR application manifest before. AIR for Android also requires a very similar manifest file, but with the addition of config which will be inserted into the native Android application manifest (native Android apps have their own manifest too and when using AIR for Android, they are effectively combined for convenience). It is here that you will need to request the specific permissions your app will require.

1
2
<!-- CrossTweet only requires the internet access permission -->
<uses-permission android:name="android.permission.INTERNET"/>

Be wary of setting the “uses-configuration” elements, they are entirely necessary if you are targeting your app at a specific type of device running Android, but if you have one set accidentally (like I originally did) your app will simply not appear in the Market via an unsupported device. It goes without saying, that I would like to see an option on Android Market to show unsupported apps with accompanying warning messages as to exactly why they’re not supported on your device!

1
2
<!-- I didn't realise this basically meant tracker ball, I originally thought a touchscreen could be classed as a five way nav! With this set, my app wouldn't appear in the Market on a Desire HD, where it would appear on a standard Desire - doh! -->
<uses-configuration android:reqFiveWayNav="true" />

By way of example, here is the full “CrossTweet-app.xml” file with additions for Android.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<application xmlns="http://ns.adobe.com/air/application/2.5" minimumPatchLevel="0">
<!-- AIR Application Descriptor File. See http://www.adobe.com/go/learn_air_1.0_application_descriptor_en. -->
    <id>com.bluebarracuda.CrossTweet</id>
    <name>CrossTweet</name>
        <!-- A string value of the format <0-999>.<0-999>.<0-999> that represents application version which can be used to check for application upgrade.
            Values can also be 1-part or 2-part. It is not necessary to have a 3-part value.
            An updated version of application must have a versionNumber value higher than the previous version. Required for namespace >= 2.5 . -->
        <versionNumber>0.2.2</versionNumber>

        <!-- A string value (such as "v1", "2.5", or "Alpha 1") that represents the version of the application, as it should be shown to users. Optional. -->
        <versionLabel>v0.2.2 Beta</versionLabel>
    <filename>CrossTweet</filename>
    <description>
        <text xml:lang="en">What is CrossTweet?
 
The large horizontal words running across the middle make up a tweet, recently sent out into the digital ether by Blue Barracuda or anyone else enjoying a life #LivingDigital.
The vertical tweets are from people across the globe, simply tweeting about their everyday life.
 
The moment the two intersect is where we live. That's the place where #LivingDigital becomes core to our outlook on life as a whole.</text>
    </description>
    <copyright>Copyright 2010, Blue Barracuda</copyright>
    <supportedProfiles>mobileDevice</supportedProfiles>
    <initialWindow>
        <content>CrossTweet.swf</content>
        <title>CrossTweet</title>
        <systemChrome>standard</systemChrome>
        <transparent>false</transparent>
        <visible>true</visible>
        <minimizable>true</minimizable>
        <maximizable>true</maximizable>
        <resizable>true</resizable>
        <width>800</width>
        <height>480</height>
        <x>150</x>
        <y>150</y>
        <minSize>800 480</minSize>
        <maxSize>800 480</maxSize>
        <autoOrients>true</autoOrients>
        <renderMode>cpu</renderMode>
    </initialWindow>
    <installFolder>Blue Barracuda/CrossTweet</installFolder>
    <programMenuFolder>Blue Barracuda/CrossTweet</programMenuFolder>
   
    <customUpdateUI>false</customUpdateUI>
    <allowBrowserInvocation>false</allowBrowserInvocation>

    <icon>
        <image36x36>icons/icon36.png</image36x36>
        <image48x48>icons/icon48.png</image48x48>
        <image72x72>icons/icon72.png</image72x72>
    </icon>

    <android>
    <manifestAdditions>
          <![CDATA[
           <manifest android:installLocation="auto">
               <!-- Added for Internet and debugging support -->
               <uses-permission android:name="android.permission.INTERNET"/>
               <supports-screens android:normalScreens="true"/>
               <uses-feature android:required="true" android:name="android.hardware.touchscreen.multitouch"/>
               <application android:enabled="true">
                   <activity android:excludeFromRecents="false">
                       <intent-filter>
                       <action android:name="android.intent.action.MAIN"/>
                       <category android:name="android.intent.category.LAUNCHER"/>
                       </intent-filter>
                   </activity>
               </application>
           </manifest>
         ]]>
      </manifestAdditions>
    </android>
   
   
</application>



ANT Buildfile

The central hub of the entire project has to be the ANT buildfile (build.xml), it neatly brings all deployments together and will be your best friend whilst testing/deploying/testing/etc.

This ANT buildfile will compile a SWF for the web as well as a SWF and APK for Android. It will create a self-signed certificate and if you have your device correctly connected to your computer, there are also tasks that will install, launch and even uninstall the app onto/from your device – c’mon, what more could you ask for?!! Don’t forget you’ll require the Flex SDK 4.1 and the latest Android 2.2 SDK at your disposal.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
<?xml version="1.0" encoding="UTF-8"?>

<!--
************************************************************

    ANT buildfile for unified codebase AIR for Android
    and Flash Player deployments.
   
    Author:         Philip Bulley
    Last updated:   4th November 2010
   
************************************************************
-->

<project default="1) Compile SWF Web" name="CrossTweet">

    <!-- Path to Flex and Android SDKs -->
    <property name="sdk_dir"            value="C:\Program Files\flex_sdk_4.1.0.16076" />
    <property name="android_sdk_dir"    value="C:\Program Files\android-sdk-windows" />
    <property name="mxmlc"              value="${sdk_dir}/lib/mxmlc.jar" />
    <property name="adl"                value="${sdk_dir}/bin/adl.exe" />
    <property name="adt"                value="${sdk_dir}/lib/adt.jar" />
    <property name="adb"                value="${android_sdk_dir}/tools/adb.exe" />
    <property name="sdk_lib_dir"        value="${sdk_dir}\frameworks\libs" />

    <!-- Project properties -->
    <property name="app_name"           value="CrossTweet" />
    <property name="app_id"             value="com.yourcompany.CrossTweet" />       <!-- must match the id in the application descriptor -->
    <property name="app_root_dir"       value="." />
    <property name="assets_dir_name"    value="asset" />
   
    <!-- Certificate properties -->
    <property name="cert_name"          value="CrossTweetCert" />
    <property name="cert_pw"            value="INSERT_PASSWORD_HERE" />
    <property name="org_name"           value="Blue Barracuda" />
    <property name="org_unit"           value="Creative" />
    <property name="country"            value="GB" />
    <property name="key_type"           value="2048-RSA" />
    <property name="cert_validity"      value="25" />
    <property name="cert_path"          location="${app_root_dir}/${assets_dir_name}/certificate" />
    <property name="cert_file"          value="${cert_path}/${cert_name}.p12" />
   
    <!-- Project paths and filenames -->
    <property name="swf_filename"               value="${app_name}.swf" />
    <property name="android_filename"           value="${app_name}.apk" />
    <property name="app_descriptor_filename"    value="${app_name}-app.xml" />
    <property name="main_class"                 value="${app_root_dir}/src/Main.as" />
    <property name="bin_dir"                    location="${app_root_dir}/bin" />
    <property name="bin_debug_dir"              location="${app_root_dir}/bin" />           <!-- note using single bin + bin-debug dir, change if you like -->
    <property name="bin_android_dir"            location="${app_root_dir}/bin-android" />
    <property name="bin_debug_android_dir"      location="${app_root_dir}/bin-android" />   <!-- note using single bin + bin-debug dir, change if you like -->
    <property name="assets_dir"                 location="${app_root_dir}/${assets_dir_name}" />
    <property name="lib_dir"                    location="${app_root_dir}/lib" />
   

    <!-- Compile SWF to build-directory for packaging -->
    <target name="1) Compile SWF Web">
        <java jar="${mxmlc}" fork="true" failonerror="true">
            <arg value="-debug=false" />
            <arg value="+flexlib=${sdk_dir}/frameworks" />
            <arg value='-static-link-runtime-shared-libraries=true' />
            <arg value='-target-player=10.0' />
            <arg value="-file-specs=${main_class}" />
            <arg value="-output=${bin_dir}/${swf_filename}" />
            <arg value='-define+=CONFIG::ANDROID,false' />
            <arg value="-library-path=${lib_dir}/as3corelib.swc" />
            <arg value="-library-path=${lib_dir}/CasaLib_1.3.0.swc" />
            <arg value="-library-path=${lib_dir}/greensock.swc" />
            <arg value="-library-path=${lib_dir}/Library.swc" />
            <arg value="-library-path=${lib_dir}/MouseManager.swc" />
            <arg value="-library-path=${lib_dir}/robotlegs-framework-v1.1.2.swc" />
        </java>
    </target>
   
    <target name="1a) Compile SWF Android">
        <java jar="${mxmlc}" fork="true" failonerror="true">
            <arg value="-debug=false" />
            <arg value="+flexlib=${sdk_dir}/frameworks" />
            <arg value="+configname=air" />
            <arg value="-file-specs=${main_class}" />
            <arg value="-output=${bin_android_dir}/${swf_filename}" />
            <arg value='-define+=CONFIG::ANDROID,true' />
            <arg value="-library-path=${lib_dir}/as3corelib.swc" />
            <arg value="-library-path=${lib_dir}/CasaLib_1.3.0.swc" />
            <arg value="-library-path=${lib_dir}/greensock.swc" />
            <arg value="-library-path=${lib_dir}/Library.swc" />
            <arg value="-library-path=${lib_dir}/MouseManager.swc" />
            <arg value="-library-path=${lib_dir}/robotlegs-framework-v1.1.2.swc" />
        </java>
    </target>

    <!-- Compile SWF to debug directory and copy assets to it -->
    <target name="2) Compile SWF Web (Debug)">
        <java jar="${mxmlc}" fork="true" failonerror="true">
            <arg value="-debug=true" />
            <arg value="+flexlib=${sdk_dir}/frameworks" />
            <arg value='-static-link-runtime-shared-libraries=true' />
            <arg value='-target-player=10.0' />
            <arg value="-file-specs=${main_class}" />
            <arg value="-output=${bin_debug_dir}/${swf_filename}" />
            <arg value='-define+=CONFIG::ANDROID,false' />
            <arg value="-library-path=${lib_dir}/as3corelib.swc" />
            <arg value="-library-path=${lib_dir}/CasaLib_1.3.0.swc" />
            <arg value="-library-path=${lib_dir}/greensock.swc" />
            <arg value="-library-path=${lib_dir}/Library.swc" />
            <arg value="-library-path=${lib_dir}/MouseManager.swc" />
            <arg value="-library-path+=${lib_dir}/robotlegs-framework-v1.1.2.swc" />
        </java>
    </target>
   
    <target name="2a) Compile SWF Android (Debug)">
        <java jar="${mxmlc}" fork="true" failonerror="true">
            <arg value="-debug=true" />
            <arg value="+flexlib=${sdk_dir}/frameworks" />
            <arg value="+configname=air" />
            <arg value="-file-specs=${main_class}" />
            <arg value="-output=${bin_debug_android_dir}/${swf_filename}" />
            <arg value='-define+=CONFIG::ANDROID,true' />
            <arg value="-library-path=${lib_dir}/as3corelib.swc" />
            <arg value="-library-path=${lib_dir}/CasaLib_1.3.0.swc" />
            <arg value="-library-path=${lib_dir}/greensock.swc" />
            <arg value="-library-path=${lib_dir}/Library.swc" />
            <arg value="-library-path=${lib_dir}/MouseManager.swc" />
            <arg value="-library-path+=${lib_dir}/robotlegs-framework-v1.1.2.swc" />
        </java>
    </target>
   
   

    <!-- Show application without packaging -->
    <target name="3) Test Android App" depends="2a) Compile SWF Android (Debug)">
        <exec executable="${adl}">
            <arg value="${bin_debug_android_dir}/${app_descriptor_filename}" />
            <arg value="${bin_debug_android_dir}" />
        </exec>
    </target>


    <!-- Packaging the application to an air-file & save it in the publish directory -->
    <target name="4) Package Android App" depends="1a) Compile SWF Android">
        <java jar="${adt}" fork="true" failonerror="true">
            <arg value="-package" />
            <arg value="-target" />
            <arg value="apk" />
            <arg value="-storetype" />
            <arg value="pkcs12" />
            <arg value="-keystore" />
            <arg value="${cert_file}" />
            <arg value="-storepass" />
            <arg value="${cert_pw}" />

            <arg value="${bin_android_dir}/${android_filename}" />
            <arg value="${bin_android_dir}/${app_descriptor_filename}" />
            <arg value="-C" />
            <arg value="${bin_android_dir}" />
            <arg value="${swf_filename}" />

            <arg value="${bin_android_dir}/icons" />
        </java>
    </target>
   
    <target name="4a) Install Android App on Device" depends="4) Package Android App">
        <java jar="${adt}" fork="true" failonerror="true">
            <arg value="-installApp" />
            <arg value="-platform" />
            <arg value="android" />
            <arg value="-platformsdk" />
            <arg value="${android_sdk_dir}" />
            <arg value="-package" />
            <arg value="${bin_android_dir}/${android_filename}" />
        </java>
    </target>
   
    <target name="4b) Launch Android App on Device">
            <java jar="${adt}" fork="true" failonerror="true">
                <arg value="-launchApp" />
                <arg value="-platform" />
                <arg value="android" />
                <arg value="-platformsdk" />
                <arg value="${android_sdk_dir}" />
                <arg value="-appid" />
                <arg value="${app_id}" />
            </java>
        </target>
   
    <target name="4c) Uninstall Android App from Device">
        <java jar="${adt}" fork="true" failonerror="true">
            <arg value="-uninstallApp" />
            <arg value="-platform" />
            <arg value="android" />
            <arg value="-platformsdk" />
            <arg value="${android_sdk_dir}" />
            <arg value="-appid" />
            <arg value="${app_id}" />
        </java>
    </target>

    <!-- Creating a digital ID certificate -->
    <target name="5) Create New Certificate">
        <java jar="${adt}" fork="true">
            <arg value="-certificate" />
            <arg value="-cn" />
            <arg value="${cert_name}" />
            <arg value="-ou" />
            <arg value="${org_unit}" />
            <arg value="-o" />
            <arg value="${org_name}" />
            <arg value="-validityPeriod" />
            <arg value="${cert_validity}" />
           
            <arg value="-c" />
            <arg value="${country}" />
            <arg value="${key_type}" />
            <arg value="${cert_file}" />
            <arg value="${cert_pw}" />
        </java>
    </target>
   
    <target name="6) List devices attached">
        <exec executable="${adb}">
            <arg value="devices" />
        </exec>
        <echo message="echo test ${org_name}" />
    </target>
       
</project>

Another very useful reference is the Building ADOBE® AIR® Applications PDF guide. It will give you more insight on how you can configure your application, as well as recommended icon sizes, and much more.

Good luck, and if you make an AIR for Android app, I’d love to know about it.

September 25, 01:06 PM

When Adobe were preaching about seamless deployment across devices, they weren’t telling porky pies. Presenting CrossTweet for Android:

Part of the motivation behind building CrossTweet was the upcoming release of Adobe AIR for Android. A key goal was to provide seamless deployment across web and mobile by employing interaction techniques ubiquitous to mouse and touchscreen input. Which is why if you’ve seen CrossTweet on the web already, you may have noticed that the prev/next navigation had been inspired by the “drag to reload” functionality of twitter and other apps on the iPhone.

Point your phone’s QR code reader at the lovely pattern below, and give the app a test drive.

For insight and tips on how this was built, see my Tips for building AIR for Android Mobile Apps post.

November 11, 09:39 AM

During my last few days working at Blue Barracuda, I managed to find a little R&D time. The result of which has been an experimental interactive data visualisation piece known as CrossTweet.

The large horizontal words running across the middle make up a single tweet. These tweets will be pulled in directly from anybody currently featured in the @BlueBarracudaUK/livingdigital twitter list – a twitter list collating digital agencies and individuals who regularly tweet on topics related to the digital industry.

The vertical tweets are from random people across the globe, simply tweeting about their everyday lives. Each vertical tweet intersects the main tweet at a common word, providing an often interesting juxtaposition between the digital industry and those who consume it.

The beauty of random: Sardine Curry Puffs? What?

Behind the scenes we have Actionscript retrieving the @BlueBarracudaUK/livingdigital twitter list (via the twiterlist2rss proxy, as Twitter don’t currently offer syndication feeds for lists). After which, the feed is broken down into individual word chunks, with each chunk forming the basis of a new search submitted to the Twitter Search API.

A few regular expressions help lubricate the process of preparing the data before sending to the search API, as well as displaying them correctly once returned. For example, punctuation is not submitted in searches as it could severely limit the results we get back; on the flipside Twitter will sometimes return results with unexpected punctuation spliced in, which could otherwise make the process of intersecting tweets problematic.

Here are a few useful regular expressions used (made easy to write with the help of the awesome regexr):

1
2
3
4
5
6
public static const REGEX_CONTAINS_URL          :RegExp = /((https?|ftp|file):\/\/+)?(([a-z0-9]+\.)+[a-z]{2,6}(\b|\/|\:))([a-z0-9\?\/~%&amp;$+\-:;=@_.,#]+[a-z0-9\/~$+\-_])?/gi;
public static const REGEX_IS_URL                :RegExp = /^((https?|ftp|file):\/\/+)?(([a-z0-9]+\.)+[a-z]{2,6}(\b|\/|\:))([a-z0-9\?\/~%&amp;$+\-:;=@_.,#]+[a-z0-9\/~$+\-_])?$/gi;
public static const REGEX_HOST                  :RegExp = /([a-z0-9-]+\.)+[a-z]{2,6}(\b|\/|:)/gi;
public static const TWITTER_USERNAME            :RegExp = /@[a-z0-9_]+/gi;
public static const TWITTER_HASHTAG             :RegExp = /#[a-z0-9]+/gi;
public static const NON_PUNCTUATION             :RegExp = /[a-zA-Z0-9 ]/gi;

On the whole, the original idea was to create something more of a novelty that could possibly grow into being useful and informative. Hopefully, it’s currently at a stage that would make David McCandless smile.

Thanks to @monicaalaya for her help with design, and to @isomadotnet for playing that round of idea tennis that gave birth to this.

You can have a play with CrossTweet here.

October 02, 11:15 AM

NOTE: This article was first posted on the Blue Barracuda blog.


Flash On The Beach 2010 opening titles by Nando Costa

I’d like to extend my sympathies to the residents of Brighton. Not only has this week seen hoards of drunken students returning to the city (it’s Freshers’ Week!), but also the annual geek onslaught that is Flash On The Beach! To the unacquainted, it’s a 3-day conference featuring some of the world’s most prolific talent to have successfully combined the (all too often) polar opposites of ‘creative’ and ‘developer’.

There’s so much to talk about (although I’ll leave out #blowjobgate!) including a couple of advancements for the Flash Platform. Most notably, during the Adobe keynote, @thibault_imbert demonstrated GPU-based video rendering, where in a live demo, he showed full HD 1080p video playback dropping from ~50% CPU usage down to 6% after the new feature had been activated. What’s more, this was on a Mac, the platform where Flash video playback has long been criticized for being less than acceptable due to Apple not having done this sooner.

Also announced was incremental compilation in the Flash IDE (a feature that Flash Developers have enjoyed for a while when compiling using mxmlc and fcsh). Meaning that when a SWF is published, only the elements that have been changed since the last publish will require re-compilation. This will be of huge benefit to Flash Animators with FLAs containing assets of a large file size (ie. embedded audio/video/large bitmaps) as re-publishing will become to be lightning fast.

We definitely saw an emphasis on producing content for mobile this year, and rightly so, as Laura Jordan Bambach (@laurajaybee) pointed out: Mobile internet access is rapidly overtaking fixed line access, a trend particularly prevalent amongst those in developing countries along with the youngest and the poorest demographics. Sounds fairly obvious right? Obviously not, as we, the content creators have so far clearly demonstrated!

Mike Chambers (@mesh) went into technical specifics by divulging his knowledge of AIR 2.5 (beta) for Android (and coming soon to iPhone, hopefully). Top tips included:

  • Using “cacheAsBitmap” and the new “cacheAsBitmapMatrix” to force the player to composite display objects on the device’s GPU for significant performance increases.
  • Using CTextureUploadTracking on iOS to visually show where display objects are being uploaded/re-uploaded to the GPU (reminds me of “Show Redraw Regions” in the Flash Player debug version). A very useful feature, although not yet available on Android.
  • Using traditional optimization techniques such as Object Pooling; avoiding MouseEvent.MOUSE_MOVE; and using MouseEvent instead of TouchEvent where possible.

Mike also pointed out Thibault Imbert’s paper on Optimizing Performance for the Adobe Flash Platform which I’m sure will make for a great read.

There was, of course a lighter side to the conference, with top inspirational presentations from Brendan Dawes (@brendandawes), Jared Tarbell and an outright mentalist session by Cyriak Harris (@CyriakHarris) – the gifted madman behind those cows & cows & cows!

But of course, that’s only scratching the surface of what was a memorable (after all that £1 beer, I’d say ‘hazy’ is a more appropriate term) three days. For more in-depth coverage, I can only recommend the FlashMagazine.com reports: Day 1 | Day 2 | Day 3 | Elevator Pitch.

September 20, 07:31 AM

It’s always advisable to use weakly referenced event listeners in AS3. If you need to recap on why, check out Grant Skinner’s blog post. But in practice, if you’re ‘coding at the speed of thought’, or if you’ve inherited someone else’s code base, you may end up with some calls to addEventListener() which don’t use a weak reference.

The following regular expression will find all occurrences of addEventListener() that don’t use a weak reference:

1
addEventListener( *)\([a-zA-Z._ ]*,[a-zA-Z._ ]*\)

Using this with your code editor’s “Find in Files…” feature (ensure you check the “Use Regular Expression” option) will track down each offending code snippet, which you can then manually amend should you decide that using a weak reference is suitable.

Example of using the regular expression in the FDT "File Search" dialogue

Just make sure that you’re not relying on the reference created by addEventListener() to prevent the garbage collector from devouring your object for dinner. Generally, using a weak reference is fine when adding an event listener to a class scoped object, but not to an object locally defined within your current method.

Feel free to test/demo/fork it at http://regexr.com?2s5fc

October 19, 06:08 AM

I’ve got Facebook games coming out of my ears right about now! This one is to accompany the new movie ‘The Girl Who Played With Fire’ – out in cinemas later this month.

Ooh! Looks like I got one right =)

If you’ve read any of the Millennium Trilogy books, you’ll know that the story’s protagonist, Lisbeth Salander (aka ‘The Wasp’), has the unnatural talent of knowing other people’s darkest secrets (we won’t mention that she’s a pesky computer hacker!). Now, through the magic of social gaming, Salander is out to test your knowledge of the people around you.

Behind the scenes, the game basically data mines the Facebook profiles of your friends (sounds menacing huh!), then builds questions based on that data. If you’re anything like me, you’ll be surprised at how little you actually know about your nearest and dearest.

Satisfy your inner stalker, go Play With Fire and you might even be slapped in the face with an instant win prize! Whoop!

Make sure you tell ALL your friends about your win! After all, modesty is so Web 1.0.

Thanks to @robert_t for helping out on this insanely tight deadline – cheers dude!

September 18, 04:32 PM

Hot on the heels of ‘Pass It Around Pizza’ comes ‘Hungry For Action’; the second social game for Pizza Hut UK. Supporting the release of the new A-Team movie, the premise is simple; smack the living daylights out of as many soldiers as possible.

We were originally going to create a brash tank-based game, until we realised that'd probably turn more women (being a large wedge of our target demographic) off than John McCririck's sweaty wackers on a humid summer's evening.

Again, gameplay is Bishi Bashi-style fast-paced, and curiously addictive. Taking a cue from games like Dance Dance Revolution, you’ll need to keep rhythm (but unlike DDR, you won’t necessarily look like a twat).

Following the success of Pass It Around Pizza’s virtual pizza slices, your A-Team characters can receive virtual pizza deliveries. These contain sharable slices which ultimately lead to prizes.

Hungry Hannibal loved it when a pizza delivery order came together - uh.

So think you can kick some military ass? Get to it soldier.

September 01, 09:14 AM

UPDATE: Please see comments below for a shuffle technique using the efficient Fisher-Yates algorithm.

Whilst working on an AS3 project, I figured I needed to shuffle the order of a Vector. A quick Google search looking for a code snippet that will shuffle the order of an Array will produce many results (although I’d most likely use the nicely packaged CasaLib’s ArrayUtil.randomize()). But the same is not true (at least as of writing) for shuffling the order of a Vector.

So this post is for anyone stumbling on in from Google, requiring a quick “I can’t be bothered to think about it” solution:

1
2
3
4
5
6
7
8
9
10
11
function shuffleVector( a:Object, b:Object ):int
{
        return Math.floor( Math.random() * 3 - 1 );
}

// Some quick timeline code to test it
var v:Vector.<String> = new Vector.<String>();
v.push('one', 'two', 'three', 'four');
trace( v );    // Original
v.sort( shuffleVector );
trace( v );    // Shuffled

Just to note, Array.sort() has never won any awards for code execution speed, so it’s likely that Vector.sort() won’t either.

September 18, 04:33 PM

In the spirit of the World Cup 2010, Wagamama wanted to challenge their loyal fans, old and new, to a classic game of football keepy-uppy — but not without a twist.

Hit it, spin it, whack it against the wall — most importantly, keep it up!*

Long gone are the days of using the tried and tested boot to keep a football blissfully bouncing in the air; this is about wielding chopsticks to keep up a gyoza. Genius?! Fortunately with the help of Box2Dflash, this actually makes for a really playable game of skill.

Sadly the budget didn’t quite stretch to true Facebook integration. But fear not; you needn’t send a telegram if score-based, peer-group boasting is your thing (we sneakily hijacked the standard “facebook.com/sharer.php” functionality).

I know what you’re thinking… that you’d be some kind of ‘gyoza grandmaster’ at this game? Well then, less talk more action please! Play the game*.

* The campaign has now ended and the game is no longer live, however you can play it on the Blue Barracuda staging site.

September 18, 04:36 PM

Quite often it’s advisable to treat unique IDs as a string, particularly when using a third party API, as you never know when they may migrate from a numeric to alphanumeric format.

The Facebook Actionscript API is no exception. In a recent project, I’d already been treating user’s UIDs as a string, but got caught out when calling the GetAppUsers API method. This essentially returns the UID of each friend who has the same Facebook application installed. Instead of returning a set of string values, it returns an array of number values.

Storing this array within Actionscript doesn’t appear to be hazardous. The problems arise when sending that array via remoting (in this case using a sfAmfphp gateway).

Facebook currently employ two formats of UID:
OLD: 289204186
NEW: 100000792322346

The old format will be serialized and received by the PHP gateway as expected, whereas PHP will output the new longer format as, for example, 100000792322E+14.

Moral of the story: iterate through the array returned by GetAppUsers and cast all values to String.

1
2
3
4
5
6
7
8
9
10
public function getAppUsersComplete(event:FacebookEvent):void
{
    var uidFriends:Array = event.data['uids'];
   
    // Convert all uidFriend uids from Number to String because AMFPHP messes this up (converts large int values to float)
    for(var i:int = 0; i<uidFriends.length; i++)
    {
        uidFriends[i] = uidFriends[i].toString();
    }
}
November 03, 12:08 PM

I’ve had a few geek/creative experiemental ideas recently, one of which being a physical installation with an Oyster card interface. So figured it was high time to start playing.

For the non-Londoners amongnst you, an Oyster card is the cashless payment smartcard used for journeys on London’s buses and Underground. Most Londoners already have one, so it’d be great to leverage that.

An oyster card uses the Mifare 13.25Mhz protocol and requires a compatible reader. The most common RFID shield used with Arduino is the Parallax. Don’t buy this if it’s Oyster cards you want to read. Instead I purchased a Stronglink SL018 which works just fine.

I’ve tested it using Oyster cards predating January 2010 (Mifare Classic 1k). I’ve not yet tested the board with the newer Oyster cards (Mifare DesFire), but believe these should be readable seeing as the two cards only differ in encryption methods. UPDATE Nov 2011: I’ve been tinkering with this again, and can confirm that you can read the Mifare DesFire’s UID with the Stronglink SL018 – in fact you can read any of the following: “1K”, “Pro”, “UltraLight”, “4K”, “ProX”, “DesFire”.

So step 1 complete. Many thanks to Marc Boon for providing Arduino code to read the tags.

September 18, 04:38 PM

UPDATE: Read my “Pass It Around Pizza” article on the Blue Barracuda blog. Aimed at marketers, it looks at the benefits of using in-game incentives/virtual gifts and rewards in conjunction with the Facebook Platform.

I’ve been getting to know Facebook Connect and the AS3 API over the past few weeks, and finally I’ve been able to put what I’ve learnt into practice by creating a game for Pizza Hut UK.

Pass the ball as many times as you can in 45 seconds!

“Pass It Around Pizza” is a part of Pizza Hut UK’s World Cup 2010 promotion. Staying true to my love for the mini-gaming legend that is Bishi Bashi, I wanted to create a quick-fire football passing mini-game.

The premise is simple, make as many passes as you can in 45 seconds, oh and collect any random pizza slices that fall out of the sky! There are 8 varieties of pizza slice to collect, and once collected they can be shared on your facebook wall for your friends to collect too. The more slices you and your friends collectively gain, the more chance you have of winning prizes.

I throughly enjoyed creating this (from concept, to a 60+ page wireframes and functional spec document, right through to PureMVC-based Flash development), so much so that I missed out on a massive “company director’s card behind the bar” pub outing to get it done :( But was it worth it? Hell yeah! I’m addicted! Hopefully you’ll be too…

November 15, 11:52 AM

UPDATE Nov 2010: This requires the old REST API Facebook_library_v3.4_flash.swc, which has now been deprecated in favour of the Graph API. Please feel free to fork this for use with the new GraphAPI SWC. There is a little more info on this in the comments. I’d love to update it myself, but not sure when that might happen at the moment :-/

One of the drawbacks of the current Facebook Actionscript API is that it doesn’t come bundled with UI components, it’s simply a data API. Fortunately you can use the JavaScript Client Library’s FB.UI.FBMLPopupDialog() to render FBML overlaying your SWF (if you don’t mind using wmode=”transparent”). But still, when it comes to the FBML fb:multi-friend-selector, if you want to do anything but send out invites to your Facebook app (via a browser-redirecting POST) , you’re out of luck.

Ideally, the fb:multi-friend-selector would allow the setting of a callback which would return the UIDs of the selected friends. It would then be down to the developer to choose what to do with them.

So, I decided to recreate the fb:multi-friend-selector directly in Flash. It will allow you to input an array of uid strings and later return a FacebookUserCollection featuring the selected users. Unfortunately I haven’t created this to be a fully resizable component, it simply does what it says on the tin. Hopefully, you may find that this gets you out of a sticky situation once you realise the shortcomings of the FBML fb:multi-friend-selector.

Don't be fooled, this MultiFriendSelector is not FBML, it's Flash :)

You can download it from the milkisevil-toolbox on github. You’ll need to add the “lib/milkisevil/FacebookComponents.swc” to your project and create a new instance of the “com.milkisevil.ui.facebook.MultiFriendSelector” class.

Here’s a rough guide to how you might want to instantiate the MultiFriendSelector:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Don't forget to make the following imports where appropriate
import com.milkisevil.ui.facebook.MultiFriendSelector;
import com.milkisevil.events.StatusEventEnhanced;

// And the following inside your class
private var multiFriendSelector:MultiFriendSelector;

private function showFriendSelector():void
{
    multiFriendSelector = new MultiFriendSelector( facebook, 16 );
    multiFriendSelector.title = 'Your friends';
    multiFriendSelector.subtitle = 'Irritate the hell out of your friends!';
    multiFriendSelector.addEventListener( MultiFriendSelector.STATUS_EVENT, multiFriendSelectorStatus );
    addChild( multiFriendSelector );
    multiFriendSelector.getFriends();      
}

private function hideFriendSelector():void
{
    removeChild( multiFriendSelector );
    multiFriendSelector = null;
}

private function multiFriendSelectorStatus(event:StatusEventEnhanced):void
{
    trace('exec multiFriendSelectorStatus: ' + event.code);

    switch(event.code)
    {
        case MultiFriendSelector.CLOSE:
            hideFriendSelector();
            break;
       
        case MultiFriendSelector.SUBMIT:
            var selectedUsers:FacebookUserCollection = multiFriendSelector.getSelected();
            hideFriendSelector();
           
            var uidList:Array = [];
           
            for(var i:int = 0; i<selectedUsers.length; i++)
            {
                var facebookUser:FacebookUser = selectedUsers.getItemAt(i) as FacebookUser;
                uidList.push( facebookUser.uid );
            }
           
            // Now do some custom stuff with those uids
            myCustomMethod( uidList );
           
            break;
    }
}

Profile

Online Media | London, United Kingdom, GB

Summary

A strong creative developer with an eye for design detail and an awareness of current cultural trends and brands. A broad understanding of both graphic design and web technologies.

Ideally suited to projects which require a richness in both aesthetics and underlying functionality. Specialising in development for HTML5 and Flash websites, applications and games.

See my blogfolio at:
www.milkisevil.com
Specialties: Core HTML5 technologies (CSS3, Javascript) plus libraries including Backbone.js, Underscore.js and jQuery. Flash Actionscript 3 with Robotlegs, PHP with Codeigniter, MySQL, Amazon Web Services (AWS Console, CLI and PHP SDK), JIRA, Git and SVN, Photoshop, Illustrator, After Effects, UX prototyping and intermediate linux server admin on Ubuntu.

Experience

  • Nov 2011 - Present
    Founder, Creative & Development Director / 2Fold20 Play
    Founded a start-up social gaming company where I'm hands-on managing the technical and creative aspects of business. Including managing AWS infrastructure, backend+frontend coding right through to graphic design (except illustration) and game design. Currently working on "Enjoy! Karaoke" - a social singing platform (based around the Facebook Graph API) complete with realtime vocal pitch detection. Play at enjoykaraoke.com
  • Feb 2011 - Present
    Freelance Flash Developer / Stinkdigital
    Worked on some of the most technically challenging projects of my career for brands including Philips, Wrangler, Hugo Just Different, Diesel and SNCF. All of which have had a mass of recognition in terms of industry awards (but that's just how Stinkdigital roll!). Greatest achievement was my lead development role on the Philips Obsessed With Sound project. An interactive music video featuring dual camera angles that allows a user to single out individual musicians in a live orchestra of over 50, ultimately providing the opportunity to hear every detail. Easily the most challenging campaign site I've created in Actionscript, and certainly one of the most enjoyable to code. And as way of a small bonus, it's received widespread recognition including Gold at Cannes Lions 2012. I'm currently a little behind on keeping my blogfolio up-to-date, but you can see a few of my Stinkdigital projects here: http://www.milkisevil.com/blog/tag/stinkdigital/
  • Nov 2010 - Present
    Freelance Flash Developer / The Mill
    Leading code architecture and shell build for Meerkovo (Compare The Market). See more at http://www.milkisevil.com/blog/2011/comparethemeerkat-com-meerkovo
  • Jun 2009 - Present
    Lead Flash Developer / Blue Barracuda
    Leading Flash team (mainly contractors) working on Pizza Hut, TGI Friday’s, Getty Images, Wagamama and Momentum Films.
  • Mar 2007 - Present
    Interactive Lead / Impact Proximity (BBDO)
    Flash development on brands including Pepsi and Masterfoods; leading the Flash team and their skill development as well as pushing creative.
  • Jan 2007 - Present
    Senior Interactive Developer / Wunderman London
    Working mainly on the Ford and Land Rover brands, developing online content in support of the related ATL campaigns. A short-lived stint before deciding to leave London for the opportunities of Dubai.
  • Oct 2004 - Present
    Senior Interactive Designer / Halpern Cowan
    Creating interactive websites and viral games mainly using Flash on brands including Norwich Union, Malmaison, Ebookers, Cohn & Wolfe, Cheapestfilghts, Lastminute.com, Tamares and Halifax Bank of Scotland.
  • Feb 2004 - Present
    Multimedia Designer/Developer / Banner Corporation
    Semi-permanent freelancer working on technology brands such as HP, Blackberry, Motorola's Freescale, Hitachi and Riso
  • Sept 2003 - Present
    Freelance Multimedia Designer / Aquent
    Freelance posts included working at Halpern Cowan, NTL, Carat Interactive and Real451. Brands worked on included Adidas, Cartoon Network, Halifax Bank of Scotland, and Urbium. The majority of projects worked on were Flash based interactive design and development.
  • Jan 2001 - Present
    Multimedia Designer/Developer / studiobrite
    Working remotely on a part-time basis whilst studying at University in Leicester. Designing and developing websites for small buisnesses.
  • Aug 2000 - Present
    Web Developer / Deepend London
    Building websites specialising in client-side and server-side scripting.

Education

  • 2000 - 2003
    De Montfort University

Additional Information

Websites:

Recent tracks

  • Boreal - Brillz Remix by {'mbid': '49d461f0-3abf-422e-9569-8813e90074d7', '#text': 'Hundred Waters'}
    15 hours ago
  • Aint Nuthin - Original Mix by {'mbid': '', '#text': 'Sanxion'}
    15 hours ago
  • Calling Your Name (Skism Remix) by {'mbid': 'cc38cb15-01b5-4036-a3be-b52e270b07cb', '#text': 'Muffler'}
    15 hours ago
  • Superman by {'mbid': '1637d94f-4e53-46a6-9c27-8e8d21a0bf90', '#text': 'Mojo'}
    15 hours ago
  • Face the bass by {'mbid': 'b7a98e65-7ce4-4fab-8cf3-6b3ccda6115a', '#text': 'Frankie Knuckles'}
    17 hours ago
  • Do It Properly by {'mbid': '835f5076-6b5d-4ba1-a3fe-d81a5838a0fc', '#text': 'Adonis'}
    17 hours ago
  • Turn Up the Bass by {'mbid': '18c45abd-04fc-451b-af41-59e624fd2669', '#text': 'Fast Eddie'}
    17 hours ago
  • Pleasure Control by {'mbid': '61c6ddd4-a24f-4156-b0eb-b3ce9200dabf', '#text': 'On The House'}
    17 hours ago
  • A Path - Original Mix by {'mbid': '6bf6b3f0-3388-481f-8ae8-4023e1a76d70', '#text': 'Fingers Inc.'}
    17 hours ago
  • Godfather Of House - Original Mix by {'mbid': '4bbfc9da-2837-455c-9a0d-6510151f1dff', '#text': 'House People'}
    18 hours ago

Top tracks

abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz