<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0"><title>Boris Smus</title><link href="http://smus.com" /><updated>2012-02-14T21:57:00Z</updated><id>http://smus.com</id><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/smuscom" /><feedburner:info uri="smuscom" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry><title>Backbone and Ember</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/mxC5SSCPcL8/backbone-and-ember" /><updated>2012-02-15T09:00:00Z</updated><published>2012-02-15T09:00:00Z</published><id>http://smus.com/backbone-and-ember</id><content type="html">
      
&lt;h1&gt;
  Backbone and&amp;nbsp;Ember
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Feb. 15, 2012"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Feb. 15, 2012&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="http://emberjs.com/"&gt;Ember&lt;/a&gt; and &lt;a href="http://documentcloud.github.com/backbone/"&gt;Backbone&lt;/a&gt; are both promising JavaScript frameworks but have
completely different philosophies. In this post, I'll compare the two, both
from a practical and philosophical perspective. I'll defer to real world
experience with Backbone and &lt;a href="http://sproutcore.com"&gt;SproutCore&lt;/a&gt; (Ember's predecessor), as well as
basic experiments with Ember (full disclosure: haven't built a large Ember app
yet). I'll also supplement claims with quotes from a fantastic &lt;a href="http://goo.gl/t7gHG"&gt;conversation&lt;/a&gt;
from Freenode &lt;a href="irc://irc.freenode.net/#documentcloud"&gt;#documentcloud&lt;/a&gt; on February 3rd, 2011. For quote
context, &lt;code&gt;wycats&lt;/code&gt; is &lt;a href="http://yehudakatz.com/"&gt;Yehuda Katz&lt;/a&gt;, one of the lead developers on Ember, and
&lt;code&gt;jashkenas&lt;/code&gt; is &lt;a href="http://ashkenas.com/"&gt;Jeremy Ashkenas&lt;/a&gt;, one of the lead developers on&amp;nbsp;Backbone.&lt;/p&gt;

&lt;p&gt;Before I go into too much detail, it's pretty clear that both frameworks have
the same goal: to help developers build&amp;nbsp;apps.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;1:09 PM &lt;strong&gt;wycats&lt;/strong&gt; backbone apps are&amp;nbsp;apps&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So we have a roughly apples-to-apples comparison. Let me dive in and talk about
some philosophical&amp;nbsp;differences:&lt;/p&gt;

&lt;h2&gt;Backbone at a&amp;nbsp;glance&lt;/h2&gt;

&lt;p&gt;Backbone is a minimalist framework that builds on top of ideas from
jQuery to give some structure to web applications. It introduces
concepts of views, models, a restful sync interface, routers, etc, in
surprisingly little code. Backbone is the darling of microframework
lovers, who highly value small framework size and don't want to commit
to a single "full stack"&amp;nbsp;solution.&lt;/p&gt;

&lt;p&gt;Backbone is incredibly flexible, and doesn't impose how you views and
models should actually interact. The main benefit is that it adds some
structure to the app and provides convenient ways of listening to DOM
events and turning them into application&amp;nbsp;events.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;1:15 PM &lt;strong&gt;wycats&lt;/strong&gt; backbone is 600loc &lt;br /&gt;
  1:16 PM &lt;strong&gt;wycats&lt;/strong&gt; "here's how you should think about your app&amp;nbsp;structure"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since Backbone is so small, it leaves a lot of decisions up to the
developer. This is both a blessing (flexibility, works for me, etc) and
a curse, since support for many things is&amp;nbsp;missing.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;12:32 PM &lt;strong&gt;wycats&lt;/strong&gt; jashkenas: backbone is 600 lines of code &lt;br /&gt;
  12:32 PM &lt;strong&gt;wycats&lt;/strong&gt; jashkenas: the idea that there are things missing in it that
  are common should not be&amp;nbsp;controversial&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;Ember at a&amp;nbsp;glance&lt;/h2&gt;

&lt;p&gt;Ember has a very different history. It's an evolution of SproutCore,
which is a complete web application solution. Ember takes the core parts
of SproutCore - two-way data binding, computed properties, tight
template integration, and strips the rest off into sub-modules. Things
that come in module format are a data serialization layer (via &lt;a href="https://github.com/emberjs/data"&gt;data&lt;/a&gt;),
routing (via &lt;a href="https://github.com/emberjs-addons/sproutcore-routing"&gt;routes&lt;/a&gt;). Ember's out of the box functionality is
actually smaller than Backbone's, but it provides a higher level&amp;nbsp;abstraction.&lt;/p&gt;

&lt;p&gt;I used SproutCore back in 2008 while working as an engineer on
iWork.com. It was a SproutCore pre-1.0 release, and things were a bit
rough. Ember seems to have come clean in many ways, presenting a more
consistent template data binding solution via Handlebars, and being a
lot less monolithic SproutCore once&amp;nbsp;was.&lt;/p&gt;

&lt;p&gt;Ember aims to provide a full solution in an opinionated way. Thus,
to get the most of the framework, developers must do things in a certain
style. While you're not forced to use Handlebars, it's the only way to
get some of the compelling features the framework provides (eg. for
two-way data binding). Many of the auxillary modules such as ember-data
are designed to fit will into the existing structure of the&amp;nbsp;framework.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;1:22 PM &lt;strong&gt;wycats&lt;/strong&gt; tbranyen: ember is an end-to-end framework built on
  top of more modular&amp;nbsp;components&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is a sentiment from many people, including Backbone's founder that
much of what Ember provides is&amp;nbsp;over-engineered:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;12:28 PM &lt;strong&gt;jashkenas&lt;/strong&gt; It's Backbone's take that Ember's more complex
  data binding model, intermediate controllers, run loop etc. ... are
  all interesting approaches, but are &lt;em&gt;not&lt;/em&gt; usually helpful in building
  a real&amp;nbsp;site.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;Different&amp;nbsp;audiences&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;12:30 PM &lt;strong&gt;jashkenas&lt;/strong&gt; wycats: and I find quite the opposite --
  self-selecting sample pools, as you'd&amp;nbsp;expect.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So to summarize at a high level, there are two camps (and excuse me
while I generalize a&amp;nbsp;lot).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Lovers of micro-frameworks. JavaScript hackers seeking extra
structure in their slightly complex apps. These people are
comfortable mixing and matching frameworks, solving problems as they
come, and just want to get started&amp;nbsp;quickly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Software Engineers that are used to a deep abstraction layer and a
full service stack. These people are probably coming from native app
development and want to write very complex applications on the&amp;nbsp;web.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Basically, these two developers have different needs, come from
different programming cultures, and are maybe even writing different&amp;nbsp;applications.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;12:31 PM &lt;strong&gt;wycats&lt;/strong&gt; jashkenas: so then it's fair to say that for &lt;em&gt;some&lt;/em&gt;
  people, Ember's approach is&amp;nbsp;overkill&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Andrew says it&amp;nbsp;best:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;12:36 PM &lt;strong&gt;andrewdeandrade&lt;/strong&gt; It's really all about values. Occasionally
  I get frustrated by things backbone.js doesn't do and occasionally I
  get frustrated by things rails does that are hard to undo. My personal
  preference is to have a framework not do something and implement it
  myself than have a framework do something and figure out how to do the
  opposite. That's me. I know people who feel&amp;nbsp;differently&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;A more detailed&amp;nbsp;comparison&lt;/h2&gt;

&lt;p&gt;After engineering with SproutCore, writing an app with Backbone, and
writing small amounts of Ember (mostly samples to get a feel for data
bindings, etc), I've got some sense of the issues that you will run into
when developing a moderately complex app. I'll go over some, and
supplement them with quotes from framework&amp;nbsp;authors.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;1:28 PM &lt;strong&gt;wycats&lt;/strong&gt; jashkenas: do you disagree that the pattern "listen
  for these properties, and when any of them changes, trigger observers"
  is very common? &lt;br /&gt;
  1:26 PM &lt;strong&gt;jashkenas&lt;/strong&gt; that's correct -- you listen for changes to the source data and render computed values. not hard. &lt;br /&gt;
  1:26 PM &lt;strong&gt;wycats&lt;/strong&gt; jashkenas: yes… a pattern that happens sufficiently often that it's good to&amp;nbsp;abstract&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In general, Ember seeks to find common problems that developers face,
and solve them in an opinionated way. Backbone, on the other hand,
leaves it to developers solve their own problems in the way that works
for&amp;nbsp;them.&lt;/p&gt;

&lt;h3&gt;Templates&lt;/h3&gt;

&lt;p&gt;While Ember in theory lets you pick which templating engine to use, you
lose a lot of benefits if you're not using&amp;nbsp;Handlebars.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;1:29 PM &lt;strong&gt;jashkenas&lt;/strong&gt; forcing your users to use logic-less templates is &lt;em&gt;incredibly&lt;/em&gt; constraining. &lt;br /&gt;
  1:30 PM &lt;strong&gt;tomdale&lt;/strong&gt; jashkenas: ember supports any templating language you'd like. you just don't get auto-updating &lt;br /&gt;
  1:30 PM &lt;strong&gt;jashkenas&lt;/strong&gt; right,&amp;nbsp;exactly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In practice, my last project used Mustache because it was logic-less.
Such templating engines are my preference, and I'm happy to be
constrained to a pretty good templating system if that means access to
powerful features, cleaner application code and less&amp;nbsp;boilerplate.&lt;/p&gt;

&lt;p&gt;I don't think that Ember should be toting choice of template as a big
benefit. Like any application framework, Ember is a contract between
developer and framework author. You write in our style and we'll give
you powerful features. In an ideal world, this lets developers focus on
their app instead of dealing with&amp;nbsp;middleware.&lt;/p&gt;

&lt;h3&gt;Views&lt;/h3&gt;

&lt;p&gt;view decomposition is one of the first tasks a front end developer
faces. They need to decide which parts of their app will be implemented
with what view. Should each list item be its own view? Should the list
be a single monolithic&amp;nbsp;view?&lt;/p&gt;

&lt;p&gt;In my experience writing Backbone apps, views are very primitive and
tend to cause issues. There's no support for any sort of view nesting,
which is totally critical for large applications with complex UIs. In
contrast, Ember provides an easy way of nesting views inside one&amp;nbsp;another.&lt;/p&gt;

&lt;p&gt;The other thing you'll notice with Backbone is that there's a lot of
micro-management required when building Backbone views. They need to
be properly cleaned up by hand, otherwise you end up with &lt;a href="http://stackoverflow.com/questions/7125402/backbone-bind-multi-event-to-one-button-after-i-new-view-multi-times"&gt;zombie
views&lt;/a&gt; bound to events, or events that &lt;a href="http://stackoverflow.com/questions/7348988/backbone-js-events-not-firing-after-re-render"&gt;don't fire at all&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some backbone projects, such as the &lt;a href="https://github.com/tbranyen/backbone.layoutmanager"&gt;layout manager&lt;/a&gt;, aim to remedy
some of these limitations by creating a Layout abstraction, that allows
nested views and handles a lot of the rendering. This is a very
interesting project, but I haven't tried it&amp;nbsp;yet.&lt;/p&gt;

&lt;h3&gt;MVC?&lt;/h3&gt;

&lt;p&gt;Ember is a traditional MVC framework, where it's clear which parts are
the view, the controller and the model. Backbone on the other hand, is
explicitly not an MVC. It never even claims to&amp;nbsp;be!&lt;/p&gt;

&lt;p&gt;In fact, if you read the &lt;a href="http://documentcloud.github.com/backbone/"&gt;main page&lt;/a&gt;, you'll notice that
there's no mention of controllers anywhere. Don't get me wrong: Backbone
still gives your applications structure, but has no opinion on the
gluing layer between data and presentation. I think Backbone once had
controllers, but they were renamed to routers, designed primarily for
handling URLs and&amp;nbsp;history/pushState.&lt;/p&gt;

&lt;p&gt;In my experience, there's very much a need for a controller when writing
even moderately complex apps. You're presented with several&amp;nbsp;options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write controller code in&amp;nbsp;views&lt;/li&gt;
&lt;li&gt;Write controller code in&amp;nbsp;models&lt;/li&gt;
&lt;li&gt;Write controller code in a&amp;nbsp;router&lt;/li&gt;
&lt;li&gt;Write your own controller&amp;nbsp;infrastructure&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you care about separation of concerns, none of these options are
really&amp;nbsp;acceptable.&lt;/p&gt;

&lt;h3&gt;Data and&amp;nbsp;servers&lt;/h3&gt;

&lt;p&gt;Backbone packs a huge punch in a small package. It comes with
&lt;code&gt;Backbone.sync&lt;/code&gt;, which lets you fully customize how you want to interact
with the&amp;nbsp;server.&lt;/p&gt;

&lt;p&gt;Following the CRUD pattern, Backbone lets you specify the response
format of an Read, but unfortunately doesn't allow you to fully
customize how you would like to serialize Create and Update and Delete
payloads for calling your server-side API. For my last project, I ended
up just completely redefining Backbone.sync, which is very powerful, but
I needed to write a lot of boilerplate to make it work&amp;nbsp;reasonably.&lt;/p&gt;

&lt;p&gt;The problem with Backbone's approach is that it doesn't separate two
parts of data stores: internal collection management and the interface
with the backend&amp;nbsp;API.&lt;/p&gt;

&lt;p&gt;Ember data, on the other hand, has the concept API adapters, which let
you specify the interface the server in response to the inner workings
of the adapter. And, additionally it has a DataStore model. I haven't
experimented with this in Ember yet, but SproutCore's version of this
worked quite&amp;nbsp;well.&lt;/p&gt;

&lt;h3&gt;Performance&amp;nbsp;considerations&lt;/h3&gt;

&lt;p&gt;When I worked on iWork.com, we definitely had some issues with
SproutCore performance for large amounts of data. The problem was that
we couldn't really optimize our code because we were locked into the way
SproutCore does things, so our way forward was to patch SproutCore
itself to address some of the performance issues, or break out of the
framework and implement the performance intensive part&amp;nbsp;manually.&lt;/p&gt;

&lt;p&gt;Jeremy voices this concern&amp;nbsp;here:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;1:33 PM &lt;strong&gt;jashkenas&lt;/strong&gt; finally, and perhaps most importantly, embers tight
  coupling of handlebars-ui-with-very-specific-bindings-to-ember-models is
  trouble, performance-wise. You can't build the really intensive parts of
  your UI with that level of binding / dom&amp;nbsp;tweaking.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And I agree with him. It's very legitimate concern for large apps, and
Yehuda didn't answer adequately in my&amp;nbsp;opinion.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;1:34 PM &lt;strong&gt;wycats&lt;/strong&gt; jashkenas: exposing the performance question to the
  user is trouble &lt;br /&gt;
  1:34 PM &lt;strong&gt;wycats&lt;/strong&gt; over the long haul Ember will be able to heuristically
  decide how bulky to update &lt;br /&gt;
  1:34 PM &lt;strong&gt;jashkenas&lt;/strong&gt; sufficiently smart compiler, eh? &lt;br /&gt;
  1:34 PM &lt;strong&gt;wycats&lt;/strong&gt; jashkenas:&amp;nbsp;:P  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm a bit concerned about this point and would love to hear a less
handwavy answer from&amp;nbsp;Yehuda.&lt;/p&gt;

&lt;h2&gt;Final thoughts&amp;nbsp;(Backbone)&lt;/h2&gt;

&lt;p&gt;My conclusion from writing an app with Backbone, and attending a Bocoup
training workshop, is that Backbone by itself is not sufficient for
building complex web apps. You will invariably go one of two&amp;nbsp;directions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Engineer a lot of stuff on top of it,&amp;nbsp;or&lt;/li&gt;
&lt;li&gt;Use existing Backbone plugins of various maturity and hope they work
well&amp;nbsp;together.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Based on my experience with jQuery, and the mess of ensuing plugins, The
latter seems overly optimistic. So basically, be prepared to write a lot
of extra &lt;a href="https://github.com/tbranyen/backbone-boilerplate"&gt;boilerplate&lt;/a&gt; code. But if you're a JavaScript developer, you
can handle&amp;nbsp;that!&lt;/p&gt;

&lt;p&gt;In practice, it's very difficult to remain productive if you're writing
both an app and a framework at the same time. Unfortunately this was my
tendency when using Backbone. I hate reinventing the wheel. Especially
if it's kind of&amp;nbsp;lopsided.&lt;/p&gt;

&lt;p&gt;That said, Backbone is fantastic for mid-to-low complexity applications
that want to maintain structured&amp;nbsp;code.&lt;/p&gt;

&lt;h2&gt;Final thoughts&amp;nbsp;(Ember)&lt;/h2&gt;

&lt;p&gt;Ember, on the other hand, forces you into its way of doing things. This
is a framework with opinions that gives you less flexibility. However,
if you grit your teeth a little bit and buy in, you'll be exposed to a
well thought out set of libraries that work well&amp;nbsp;together.&lt;/p&gt;

&lt;p&gt;I really like the features Ember offers, and its philosophy of finding
common problems developers will face and solving them. One thing I
anticipate is that many of the non-core Ember modules are immature.
However, just having these modules that are designed to work together is
a boon for serious application&amp;nbsp;developers.&lt;/p&gt;

&lt;p&gt;I'm still concerned that Ember applications may be stuck if dealing with
particularly hairy custom view situations, or large amounts of data, but
I'll have a better sense of the limitations&amp;nbsp;soon.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/backbone-and-ember</feedburner:origLink></entry><entry><title>PDF Conversion and Client-side Scraping</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/kugCAaKJ61U/pdf-conversion-client-side-scraping" /><updated>2012-02-08T09:00:00Z</updated><published>2012-02-08T09:00:00Z</published><id>http://smus.com/pdf-conversion-client-side-scraping</id><content type="html">
      
&lt;h1&gt;
  PDF Conversion and Client-side&amp;nbsp;Scraping
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Feb. 8, 2012"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Feb. 8, 2012&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;One of my ongoing projects is called smusique, a mobile web-based sheet
music viewer. The application was designed for tablets, enabling a
rich browsing experience through sheet music. Though some sheet
music is encumbered by licenses, most of the classic stuff is legally
available online via sheet music databases such as the &lt;a href="http://imslp.org"&gt;Petrucci
Library&lt;/a&gt; at &lt;a href="http://imslp.org"&gt;imslp.org&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While the focus of my project was to design and develop a delightful
tablet-based interface for sheet music, I needed to solve some technical&amp;nbsp;problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Figure out how to deal with PDFs in the&amp;nbsp;browser.&lt;/li&gt;
&lt;li&gt;Create or find a database with a large enough corpus of searchable
sheet&amp;nbsp;music.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Read on to find out how I solved these problems using the AppEngine
conversion API and through a Chrome extension that does client-side&amp;nbsp;scraping.&lt;/p&gt;

&lt;h2&gt;Dealing with&amp;nbsp;PDFs&lt;/h2&gt;

&lt;p&gt;Since digital sheets are largely in PDF format, I needed some solution
for showing PDFs inside a web user interface. Unfortunately
showing PDFs inline is pretty much impossible using today's web. Ideally,
the browser would have support for something like the&amp;nbsp;following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;img src="something.pdf" page="3" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But this functionality simply doesn't exist in a widely supported
fasion. Given how important showing PDFs is for my application, I
needed a workaround. A few options came to&amp;nbsp;mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Attempt to show PDFs in an iframe, and programatically scroll to the
right position based on the page&amp;nbsp;number.&lt;/li&gt;
&lt;li&gt;Convert PDFs on the client side using something like &lt;a href="http://andreasgal.com/2011/06/15/pdf-js/"&gt;PDFJS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Build my own PDF conversion&amp;nbsp;server&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I experimented briefly with the first approach and unsurprisingly found
mixed levels of browser support for PDF rendering inside iframes. Some mobile
browsers allowed PDFs to be opened within iframes, but exhibited
unexpected scaling behavior, could not be programmatically scrolled, or&amp;nbsp;both.&lt;/p&gt;

&lt;p&gt;Did not seriously consider PDFJS as a solution, judging that running PDF
conversion in a mobile web browser would be prohibitively&amp;nbsp;slow.&lt;/p&gt;

&lt;p&gt;Just as I was bracing to take the plunge and write my own
&lt;a href="http://www.imagemagick.org/"&gt;ImageMagick&lt;/a&gt;-based conversion API running on Django/Slicehost, I
discovered the very new &lt;a href="http://code.google.com/appengine/docs/python/conversion/overview.html"&gt;AppEngine conversion API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;The conversion&amp;nbsp;API&lt;/h2&gt;

&lt;p&gt;Turns out that AppEngine provides a new experimental &lt;a href="http://code.google.com/appengine/docs/python/conversion/overview.html"&gt;file format
conversion API&lt;/a&gt;. It allows you to map from a variety of input file
formats to a variety of outputs, including PDF to PNGs (one per page),
which is exactly what I wanted. The full list of conversion paths is
available in &lt;a href="http://code.google.com/appengine/docs/python/conversion/overview.html"&gt;the docs&lt;/a&gt;. Some of the more exciting
features include image to text conversions via OCR, and generating images
from&amp;nbsp;HTML.&lt;/p&gt;

&lt;p&gt;My conversion was really straight forward to&amp;nbsp;implement:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def convert_pdf(self, pdf_data):
    """Converts PDF to PNG images. Returns an array of PNG data."""
    asset = conversion.Asset('application/pdf', pdf_data, 'sheet.pdf')
    conversion_request = conversion.Conversion(asset, 'image/png')
    result = conversion.convert(conversion_request)
    if result.assets:
        return [asset.data for asset in result.assets]
    else:
        raise Exception('Conversion failed: %d %s'
                % (result.error_code, result.error_text))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that the API methods recently changed (maybe in AppEngine 1.6?)
from &lt;code&gt;conversion.ConversionRequest&lt;/code&gt; to &lt;code&gt;conversion.Conversion&lt;/code&gt;. I should
have expected breaking changes since the conversion API is still
experimental, but it stumped me for a little while&amp;nbsp;anyway.&lt;/p&gt;

&lt;p&gt;The API works pretty well, but doesn't provide much meaningful feedback
if running in the dev server. So far I've only managed to overload the
conversion service a few times with very large (eg. ~100 page) PDFs.
That said, a lot of sheet music is incredibly long, especially in an
orchestral setting. So, if I was doing this for production, I would
probably need to build a custom&amp;nbsp;solution.&lt;/p&gt;

&lt;p&gt;One other caveat with this approach is that I have no idea how much
money I would be charged for using this service. Conversion is quite
intensive, and given the recently increased rates, I would be&amp;nbsp;wary.&lt;/p&gt;

&lt;p&gt;Once the images are converted, I upload them to an Amazon S3 instance
where I keep my data. To do this, I use &lt;a href="http://aws.amazon.com/code/134"&gt;S3.py&lt;/a&gt;, a really simple
library for interacting with Amazon&amp;nbsp;S3:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def upload_helper(self, path, data, contentType):
    conn = S3.AWSAuthConnection(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
    options = {'x-amz-acl': 'public-read', 'Content-Type': contentType}
    response = conn.put(DEFAULT_BUCKET, path, S3.S3Object(data), options)
    return URL_FORMAT % {'bucket': DEFAULT_BUCKET, 'path': path}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The full code for the AppEngine server is open source on &lt;a href="https://github.com/borismus/smusique-uploader"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Client-side&amp;nbsp;scraping&lt;/h2&gt;

&lt;p&gt;Unfortunately IMSLP doesn't provide a useful API out of the box. I
wasn't ambitious enough to create a full API for it, but needed some
interim solution. I figured that for a demo, it wouldn't be such a
terrible experience to have to seed the database with the repertoire you
were interested in if it was easy enough to&amp;nbsp;do.&lt;/p&gt;

&lt;p&gt;Ordinarily, one might write a little scraper utility in their favorite
scripting language. However, scraping HTML from python (or any other
scripting language for that matter) is really not my favorite activity.
Additionally, relying on the command line excludes the target audience
(musicians) for this&amp;nbsp;application.&lt;/p&gt;

&lt;p&gt;As a workaround, I came across a potentially interesting idea:
client-side scraping with a Chrome extension. Let me&amp;nbsp;explain.&lt;/p&gt;

&lt;p&gt;Suppose you want to scrape part of a corpus of data that's available
through a website, but want to let the user decide which parts they are
interested in. Simply use a Chrome extension that injects code into the
target page, fetches the interesting part of the DOM using selectors and
perhaps jQuery for convenience, and then uploads the data to some
server. I used &lt;a href="http://code.google.com/chrome/extensions/content_scripts.html"&gt;content scripts&lt;/a&gt; for this purpose. In the manifest, the
entry looks as&amp;nbsp;follows:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;"content_scripts": [{
  "matches": ["http://imslp.org/wiki/*"],
  "js": ["imslp.js"],
  "css": ["imslp.css"]
}]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, &lt;code&gt;imslp.js&lt;/code&gt; does the scraping, which can happen automatically as a
user navigates through a page, or by adding extra elements to the page.
This simple IMSLP scraper creates a "send to smusique" button beside
each PDF on&amp;nbsp;IMSLP:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://i.imgur.com/6YhZ2.png" alt="screenshot" /&gt;&lt;/p&gt;

&lt;p&gt;Once clicked, &lt;code&gt;imslp.js&lt;/code&gt; gets the URL to fetch, extracts all of the meta
data of the current piece via CSS selectors, and makes a cross-domain
request to the converter with all of this&amp;nbsp;information.&lt;/p&gt;

&lt;p&gt;This approach is much harder to prevent than traditional, server side
scraping. Although setting UserAgent restrictions is futile, many sites
use JavaScript to render their content, and serve up a content-free
page. In contrast, there's very little a content provider can do to
protect themselves from client side scraping, making this a very
powerful technique. If the content is in the DOM, it can be&amp;nbsp;extracted.&lt;/p&gt;

&lt;p&gt;The source of the extension is somewhat messy but available also on
&lt;a href="https://github.com/borismus/smusique-extension"&gt;github&lt;/a&gt;.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/pdf-conversion-client-side-scraping</feedburner:origLink></entry><entry><title>A mobile web application tech stack</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/djss64w7AfI/mobile-web-app-tech-stack" /><updated>2012-01-18T09:00:00Z</updated><published>2012-01-18T09:00:00Z</published><id>http://smus.com/mobile-web-app-tech-stack</id><content type="html">
      
&lt;h1&gt;
  A mobile web application tech&amp;nbsp;stack
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Jan. 18, 2012"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Jan. 18, 2012&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;There are many technical decisions to make when writing web applications. I've
come back to writing modern web applications lately, and wanted to consolidate
some scattered thoughts that I’ve recorded over the course of my development
cycle. In particular, this post is about the set of frameworks that I found to
be instrumental in developing my most recent project. I'll go over some of the
most important framework types, each of which could be expanded into an article
in its own right. This is not meant to be an extensive comparison of existing
offerings, just a slice of technologies that I experimented with most&amp;nbsp;recently.&lt;/p&gt;

&lt;p&gt;Although my focus is on mobile first, I think that this set of technologies can
be applied to web apps in general. All of my decisions and data points were
made with a few requirements in&amp;nbsp;mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript only (CoffeeScript, Dart, are definitely worth a serious look, but
cause an explosion in choice which I wanted to&amp;nbsp;avoid)&lt;/li&gt;
&lt;li&gt;Must work well in modern browsers (iOS 5, Android&amp;nbsp;4)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Picking an&amp;nbsp;MVC&lt;/h2&gt;

&lt;p&gt;The model view controller pattern has been in use in native UI app development
for decades. The basic idea is to separate the data layer (storage,
communication, data) from the presentation layer (UI, animation, input). There
are other similar patterns such as MVVM (Model View ViewModel), but the main idea
is to have well-defined separation between the presentation and data layers for
cleaner code and ultimately long-term&amp;nbsp;velocity:&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2012/separation.jpg" alt="separation" /&gt;&lt;/p&gt;

&lt;p&gt;There are tons of offerings of JavaScript model view controller frameworks.
Some, such as &lt;a href="http://documentcloud.github.com/backbone/"&gt;Backbone.js&lt;/a&gt; and &lt;a href="http://spinejs.com/"&gt;Spine.js&lt;/a&gt; are written in pure
code, while others like &lt;a href="http://knockoutjs.com/"&gt;Knockout.js&lt;/a&gt; and &lt;a href="http://angularjs.org/#/"&gt;Angular&lt;/a&gt; rely on
DOM data attribute binding. Relying on HTML5 data DOM attributes feels wrong
for an MVC system, whose whole point is to separate view and data. This
excludes the Knockout and Angular frameworks. Spine.js is easier with
CoffeeScript, which we exclude based on my initial&amp;nbsp;requirements.&lt;/p&gt;

&lt;p&gt;Backbone.js has been around for longer than most (except perhaps JavaScriptMVC,
seems like a dead project), and also features a growing open source community.
For my app stack, I went with Backbone.js. For more information about picking
an MVC, check out &lt;a href="http://addyosmani.github.com/todomvc/"&gt;TodoMVC&lt;/a&gt;, which implements the same Todo
application using different MVC frameworks. Also see this
&lt;a href="http://codebrief.com/2012/01/the-top-10-javascript-mvc-frameworks-reviewed/"&gt;MVC framework comparison&lt;/a&gt;, which strongly favors the
&lt;a href="http://emberjs.com/"&gt;Ember.js&lt;/a&gt;, a relative newcomer to the scene. I haven’t yet had a chance
to play with it, but it’s on my&amp;nbsp;list.&lt;/p&gt;

&lt;h2&gt;Picking a templating&amp;nbsp;engine&lt;/h2&gt;

&lt;p&gt;To build a serious application on the web, you inevitably build up large DOM
trees. Rather than using JavaScript APIs to manipulate the DOM, it can be much
simpler and more efficient to write HTML using a string-based template instead.
Generally speaking JS templates have evolved to use this at-first strange
convention of embedding the template content inside script tags: &lt;code&gt;&amp;lt;script
  id="my-template" type="text/my-template-language"&amp;gt;...&amp;lt;/script&amp;gt;&lt;/code&gt;. The basic
pattern of use for all template engines is to load the template as a string,
construct template parameters and then run the template and parameters through
the templating&amp;nbsp;engine.&lt;/p&gt;

&lt;p&gt;Backbone.js depends on &lt;a href="http://documentcloud.github.com/underscore/"&gt;Underscore.js&lt;/a&gt;, which ships with a somewhat
limited templating engine with verbose syntax. There are other options
available, including &lt;a href="http://api.jquery.com/category/plugins/templates/"&gt;jQuery Templates&lt;/a&gt;, &lt;a href="http://handlebarsjs.com/"&gt;Handlebars.js&lt;/a&gt;,
&lt;a href="http://mustache.github.com/"&gt;Mustache.js&lt;/a&gt; and many others. jQuery Templates have been deprecated
by the jQuery team, so I did not consider this option. Mustache is a
cross-language templating system, featuring simplicity and a deliberate
decision to support as little logic as possible. Indeed, the most complex
construct in Mustache is a way to iterate an array of objects. Handlebars.js
builds heavily on Mustache, adding some nice features such as template
precompilation and in-template expressions. For my purposes I didn’t need these
extra features, and chose Mustache.js as my templating&amp;nbsp;platform.&lt;/p&gt;

&lt;p&gt;In general, my impression is that the existing templating frameworks are quite
minimal and comparable in features, so the decision is largely a matter of
personal&amp;nbsp;preference.&lt;/p&gt;

&lt;h2&gt;Picking a CSS&amp;nbsp;Framework&lt;/h2&gt;

&lt;p&gt;CSS frameworks are essential tools that extend CSS’s feature set with
conveniences such as variables, a way to create hierarchical CSS selectors, and
some more advanced features. This essentially creates a new language: an
augmented version of CSS (let’s call it CSS++). For development ease, some
frameworks implement a JavaScript CSS++ interpreter in the browser, while other
frameworks let you monitor a CSS++ file and compile it whenever there are any
changes made. All CSS frameworks should provide command line tool to compile
CSS++ down to CSS for&amp;nbsp;deployment.&lt;/p&gt;

&lt;p&gt;As with templating languages, there are many choices all of which do very
similar things. My choice was motivated by personal syntax preference, and I
prefer &lt;a href="http://sass-lang.com/"&gt;SCSS&lt;/a&gt; because it avoids weird syntax like &lt;code&gt;@&lt;/code&gt;. One drawback of SCSS is
that it doesn’t ship with a JavaScript interpreter (there is an &lt;a href="https://github.com/bmavity/scss-js"&gt;unofficial one&lt;/a&gt;
that I haven’t tried), but does come with a command line watcher. Other similar
CSS frameworks include &lt;a href="http://lesscss.org/"&gt;LESS&lt;/a&gt; and &lt;a href="http://learnboost.github.com/stylus/"&gt;Stylus&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;How to layout&amp;nbsp;views&lt;/h2&gt;

&lt;p&gt;HTML5 provides a variety of ways to layout content, and MVC frameworks provide
no opinion about which of these layout technologies to use, leaving the
sometimes difficult decision to you, the&amp;nbsp;developer.&lt;/p&gt;

&lt;p&gt;Generally speaking, relative positioning is appropriate for documents, but
falls apart for apps. Absolute positioning should be avoided, as should tables,
clearly. Many web developers have turned to the float property to align
elements, but this is suboptimal for building application views, since it’s not
optimized for app-like layouts, which results in many odd problems and &lt;a href="http://stackoverflow.com/questions/8554043/what-actually-is-clearfix"&gt;infamous
clearfix hacks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After much experimentation with various web layout technologies over the years,
I think that a combination of fixed positioning and flexbox model is ideal for
mobile web applications. I use fixed positioning for UI elements that are fixed
on the screen (headers, sidebars, footers, etc). The flex box model is great
for laying out stacked views on the page (horizontally or vertically). It’s the
only CSS box model explicitly optimized for interface design, quite similar to
Android’s LinearLayout manager. For more information about the flexbox model,
read &lt;a href="http://www.html5rocks.com/en/tutorials/flexbox/quick/"&gt;Paul's article&lt;/a&gt; and note that this spec is being replaced by a
&lt;a href="http://www.w3.org/TR/css3-flexbox/"&gt;new, non-backwards compatible version&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Adaptive Web&amp;nbsp;Apps&lt;/h3&gt;

&lt;p&gt;One final section on this matter: I’m a strong proponent of creating
device-specific user interfaces. This means re-writing parts of your view code
for different form factors. Luckily, the MVC pattern makes it relatively easy
to reuse a single model for multiple views (eg. tablet and&amp;nbsp;phone).&lt;/p&gt;

&lt;p&gt;Flipboard for iOS demonstrates this idea very well, giving tablet and phone
users a highly tailored experience for each device form&amp;nbsp;factor.&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2012/flipboard-phone.jpg" alt="flipboard-phone" /&gt;
The phone UI is optimized for vertical swipes, allowing single hand&amp;nbsp;use.&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2012/flipboard-tablet.jpg" alt="flipboard-tablet" /&gt;
Tablet UI works well for two hands holding the device on opposite&amp;nbsp;sides.&lt;/p&gt;

&lt;h2&gt;Input&amp;nbsp;considerations&lt;/h2&gt;

&lt;p&gt;On mobile, the main way users interact with your application is by touching the
screen with their fingers. This is quite different from mouse-based
interaction, since there are 9 additional points to track on the screen, which
means developers need to move away from mouse events when writing mobile apps.
In addition, mouse events on mobile have the problem of clicks being delayed by
300ms (there is a well-known &lt;a href="http://code.google.com/mobile/articles/fast_buttons.html"&gt;touch-based workaround&lt;/a&gt;). For more information
about using these events in mobile browsers, see &lt;a href="http://www.html5rocks.com/en/mobile/touch.html"&gt;my touch events article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s not enough to just &lt;code&gt;s/mousedown/touchstart/&lt;/code&gt; all of your event handlers.
There is a completely new set of gestures that users have come to expect on
touch devices, such as swipes to, for example, navigate through lists of
images. Though Apple has a little-known &lt;a href="http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/GestureEventClassReference/GestureEvent/GestureEvent.html#//apple_ref/doc/uid/TP40009353"&gt;gestures API&lt;/a&gt;, there is no open spec for
doing gesture detection on the web. We really need a JavaScript library to do
gesture detection, for some of the &lt;a href="http://www.lukew.com/touch/TouchGestureGuide.pdf"&gt;more common gestures&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;How to make it work&amp;nbsp;offline&lt;/h2&gt;

&lt;p&gt;For an app to work offline, you need two things to be&amp;nbsp;true:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Assets are available (via AppCache, Filesystem API,&amp;nbsp;etc)&lt;/li&gt;
&lt;li&gt;Data is available (via LocalStorage, WebSQL, IndexedDB,&amp;nbsp;etc)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In practice, building offline apps on the web is a difficult problem. Generally
speaking offline functionality should be built into your app from the
beginning. It’s especially difficult to offline-ify an existing web application
without significant code rewriting. Additionally, there are often unknown
storage limits for various offline technologies, and undefined behavior for
what happens when those limits are exceeded. Finally, there are problems with
technologies in the offline technology stack, most notably AppCache, as I
outlined in a &lt;a href="http://smus.com/game-asset-loader"&gt;previous post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A very interesting approach to write truly offline-capable apps is to go
“offline first”. In other words, write everything as if you have no internet
connection, and implement a syncing layer that synchronizes data when an
internet connection exists. In the Backbone.js MVC model, this can fit nicely
as a custom &lt;code&gt;Backbone.sync&lt;/code&gt; adapter.&lt;/p&gt;

&lt;h2&gt;Unit&amp;nbsp;testing&lt;/h2&gt;

&lt;p&gt;It’s hard to unit test your UI. However, since you’re using an MVC, the model
is completely isolated from the UI and as a result, easy to test. &lt;a href="http://docs.jquery.com/QUnit"&gt;QUnit&lt;/a&gt; is
quite a nice option, especially because it allows to unit test asynchronous
code using it’s &lt;a href="http://docs.jquery.com/QUnit/start#decrement"&gt;start() and stop()&lt;/a&gt;&amp;nbsp;methods.&lt;/p&gt;

&lt;h2&gt;Signing&amp;nbsp;off&lt;/h2&gt;

&lt;p&gt;To summarize, I used Backbone.js for MVC, Mustache.js for templating, SCSS for
a CSS framework, CSS Flexbox to render views, custom touch events and QUnit for
unit testing to write my mobile web application. For offline support, I’m still
experimenting with various technologies and will hopefully follow up with more
information a future post. While I strongly believe in the need for each class
of tool (eg. MVC) outlined here, I also believe that many of the specific
technologies I described here are interchangeable (eg. Handlebars and&amp;nbsp;Mustache).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One more thing&lt;/strong&gt;: yesterday (on January 17th, 2012), &lt;a href="http://walmartlabs.github.com/thorax/"&gt;Thorax&lt;/a&gt; was
announced. This is a Backbone-based set of libraries very similar in spirit to
what I describe in this post. I've yet to investigate it in any depth, but the
name is great&amp;nbsp;:)&lt;/p&gt;

&lt;p&gt;Use a similar set of frameworks? Have a personal favorite? Think I’m missing an
important type of framework? Let me&amp;nbsp;know!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/mobile-web-app-tech-stack</feedburner:origLink></entry><entry><title>Hybrid Operating Systems</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/hUNnnHTGQd8/hybrid-operating-systems" /><updated>2011-11-28T09:00:00Z</updated><published>2011-11-28T09:00:00Z</published><id>http://smus.com/hybrid-operating-systems</id><content type="html">
      
&lt;h1&gt;
  Hybrid Operating&amp;nbsp;Systems
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Nov. 28, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Nov. 28, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;I've been thinking a bit about native and web applications, and how they can
(or can't) coexist in the future. This topic has been steeping in my head for
months now, so here is a brain dump of some of my&amp;nbsp;thoughts.&lt;/p&gt;

&lt;h2&gt;On hybrid&amp;nbsp;apps&lt;/h2&gt;

&lt;p&gt;Hybrid apps embed a browser, using a web view for part of their UI.
This is a very flexible definition for a very flexible beast. At one
extreme are native apps that embed a Web View to render a small widget
(written in HTML/CSS/JS) in the UI. On the other hand, you could have a
web application that provides just a native frame around the content
which is entirely implemented as a web app. The hybrid app spectrum
looks something like&amp;nbsp;this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;native &amp;lt;--------------- hybrid apps ---------------&amp;gt; web
Mail.app                GMail for iOS              GMail
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Pieces of the web stack have long been useful as building blocks for
applications, but lately there has been a bloom in features under the
HTML5 moniker. As a result, many developers are gravitating towards the
right end of the hybrid app&amp;nbsp;spectrum.&lt;/p&gt;

&lt;p&gt;There are several frameworks which target this niche by providing thin native
frames, such as &lt;a href="http://phonegap.com/"&gt;PhoneGap&lt;/a&gt; and &lt;a href="http://fluidapp.com/"&gt;Fluid&lt;/a&gt;. There are three benefits to these
frameworks, helping developers&amp;nbsp;to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make money: capitalize on AppStore and Market&amp;nbsp;earnings.&lt;/li&gt;
&lt;li&gt;Use more features: get access to features not available from the&amp;nbsp;web.&lt;/li&gt;
&lt;li&gt;Provide good platform-specific native&amp;nbsp;integration.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first two are relatively well understood, so for the purposes of this post,
I'm more interested in the third. Android PhoneGap apps are launched from the
home screen, Fluid Mac apps from spotlight or your dock, etc. Then you can
switch between these apps as if they were regular OS X&amp;nbsp;applications.&lt;/p&gt;

&lt;p&gt;Even though the browser is a single app, it runs tons of applications
like GMail, twitter, etc. The browser allows people to do two
fundamentally different things: view content on the web (sites), and &lt;em&gt;do
things&lt;/em&gt;, like listen to music, play games and create documents (apps).
For a deeper discussion on the distinctions between web apps and web
sites, check out James Pearce's article &lt;a href="http://tripleodeon.com/2011/09/of-sites-and-apps/"&gt;Of Sites and Apps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A pure web application does not rely on platform-specific native code
and runs inside the browser, but still has to rely on a native frame
solution to reap the native integration benefit frameworks like PhoneGap
and Fluid&amp;nbsp;provide.&lt;/p&gt;

&lt;h2&gt;Hybrid operating&amp;nbsp;systems&lt;/h2&gt;

&lt;p&gt;Some browsers today have an explicit notion of apps. Without the proper OS
integration, this is confusing. Now your device (laptop, tablet, phone, tv) has
apps, one of which is a browser, and the browser has apps too. So to launch an
app, users have to open the browser, and then launch what they want. This
reinforces the distinction between web and native apps in people's heads and
makes for a very inelegant&amp;nbsp;solution.&lt;/p&gt;

&lt;p&gt;One approach is for the OS to make the browser special in the operating system,
allowing it also to manage installed web applications. Another approach is for
operating systems to implement their SDKs using HTML, CSS and JavaScript, but
with non-standard APIs specific to the platform, such as WebOS. The extreme of
this approach is Chrome OS, where browser and OS are&amp;nbsp;indistinguishable.&lt;/p&gt;

&lt;p&gt;Like apps, operating systems vary in how much they embrace web applications.
Here is an interesting spectrum to think&amp;nbsp;about:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;web agnostic &amp;lt;------------ hybrid OS ------------&amp;gt; apps are webapps
iOS           Windows 8              Web OS               Chrome OS
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let me define what I mean by "hybrid OS". A hybrid OS is aware of the
presence of not just native, but also web applications, and provides
ways of managing web apps, possibly alongside native ones. Windows 8 is an
example of a hybrid OS, employing the new Metro UI, while also supporting
Windows 7-style UI. Palm's Web OS, has no native mode at all (except via
&lt;a href="https://developer.palm.com/content/api/dev-guide/pdk/overview.html"&gt;PDK plugins&lt;/a&gt;), and all apps are web apps (though they require the use of a
special set of JavaScript&amp;nbsp;libraries).&lt;/p&gt;

&lt;p&gt;Web apps today are actively used, in some cases, even &lt;a href="http://www.readwriteweb.com/archives/financial_times_proves_html5_can_beat_native_mobil.php"&gt;surpassing&lt;/a&gt; native
apps in popularity. Unfortunately, users of web apps are stuck in their browser
which is confusing and limiting, given the current landscape of native
applications. Hybrid operating systems make explicit the idea that in the end,
web apps are just&amp;nbsp;apps.&lt;/p&gt;

&lt;h2&gt;Future&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;The second goal of PhoneGap is for the project to cease to exist. This is not
  a nihilistic sentiment... -- &lt;a href="http://wiki.phonegap.com/w/page/46311152/apache-callback-proposal"&gt;Apache Callback&amp;nbsp;Proposal&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My goal is for web apps to become compelling enough to force OS creators to
hybridize their platforms. In other words, I'd like to see rightward movement
in both the app and OS spectrums. As more hybrid operating systems emerge, we
get closer to PhoneGap's second&amp;nbsp;goal.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/hybrid-operating-systems</feedburner:origLink></entry><entry><title>Stack Exchange question notifier</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/1GPpAHUgdT4/stack-exchange-question-notifier" /><updated>2011-11-15T09:00:00Z</updated><published>2011-11-15T09:00:00Z</published><id>http://smus.com/stack-exchange-question-notifier</id><content type="html">
      
&lt;h1&gt;
  Stack Exchange question&amp;nbsp;notifier
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Nov. 15, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Nov. 15, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;I've been a member of the Chrome developer relations team for the last 9
months, where one aspect of my job is developer support. I actively
monitor the chromium-extensions@chromium.org list, answering questions
about developing Chrome extensions. Thankfully, there are many others that
help me in this endeavor, both from the Chrome team and from within the
developer community itself, notably a certain &lt;a href="http://goo.gl/di3kR"&gt;PhistucK&lt;/a&gt;, the current
record holder for the number of messages posted to the&amp;nbsp;list.&lt;/p&gt;

&lt;p&gt;Over the last years, another channel of questions has emerged:
&lt;a href="http://stackoverflow.com/"&gt;Stack Overflow (SO)&lt;/a&gt;. SO is a great Q&amp;amp;A community for
software developers, providing many advantages over ordinary discussion
groups. Google has recognized this and sponsored SO tags,
such as &lt;a href="http://stackoverflow.com/questions/tagged/google-chrome"&gt;google-chrome&lt;/a&gt;, and &lt;a href="http://stackoverflow.com/questions/tagged/google-chrome-extension"&gt;google-chrome-extension&lt;/a&gt;. As I
outlined in an mail to the group, we are now including SO as
an official Chrome extension developer support&amp;nbsp;channel.&lt;/p&gt;

&lt;p&gt;As part of optimizing the support workflow, I wrote a Chrome extension
which monitors questions with certain tags on SO and other Stack
Exchange sites. This extension implements a browser action which gets
badged with the number of unreviewed&amp;nbsp;questions.&lt;/p&gt;

&lt;p&gt;The extension UI uses the same visual style as other Google apps, and
blends well into Chrome. I think it works reasonably well from a design
perspective, both in the options page and in a browser&amp;nbsp;action:&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2011/stack-screenshot.png" alt="screenshot" /&gt;&lt;/p&gt;

&lt;p&gt;If you're curious to see how the extension is implemented, the source
&lt;a href="https://github.com/borismus/Question-Monitor-for-Stack-Exchange"&gt;is available&lt;/a&gt; free of charge! This extension was implemented in
&lt;a href="http://code.google.com/closure/"&gt;Closure-style&lt;/a&gt;&amp;nbsp;JavaScript.&lt;/p&gt;

&lt;p&gt;If you support developers on Stack Overflow, please &lt;a href="https://chrome.google.com/webstore/detail/bnnkhapbhkejookmhgpgaikfdoegkmdp"&gt;install it&lt;/a&gt;,
give me constructive feedback and consider monitoring the
&lt;a href="http://stackoverflow.com/questions/tagged/google-chrome-extension"&gt;google-chrome-extension&lt;/a&gt; tag&amp;nbsp;:)&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/stack-exchange-question-notifier</feedburner:origLink></entry><entry><title>UIST 2011 Greatest Hits</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/fv2iikG-TE8/uist-2011" /><updated>2011-10-20T09:00:35Z</updated><published>2011-10-20T09:00:35Z</published><id>http://smus.com/uist-2011</id><content type="html">
      
&lt;h1&gt;
  UIST 2011 Greatest&amp;nbsp;Hits
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Oct. 20, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Oct. 20, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;I went to UIST 2011 in Santa Barbara and presented our research on
&lt;a href="http://crowdforge.com"&gt;CrowdForge&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's a sample of some of the great work that was presented this year, in the
3 research areas that interest me most: crowdsourcing/human computation,
mobile physical computing, and&amp;nbsp;music.&lt;/p&gt;

&lt;h1&gt;Crowdsourcing&lt;/h1&gt;

&lt;p&gt;Notable work in both workflow-oriented approaches (Jabberwocky, CrowdForge,
PlateMate) and synchronous collaborative approaches (Crowds in seconds,
Collabode,&amp;nbsp;Real-time).&lt;/p&gt;

&lt;h3&gt;PlateMate&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Presents a workflow-based crowdsourcing nutritional analysis from food&amp;nbsp;photographs.&lt;/li&gt;
&lt;li&gt;Implemented in Django/Python, same as&amp;nbsp;CrowdForge&lt;/li&gt;
&lt;li&gt;Found out about CrowdForge 90% of the way into the&amp;nbsp;research.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="http://crowdresearch.org/blog/wp-content/uploads/2011/09/dinner.jpg" alt="platemate" /&gt;&lt;/p&gt;

&lt;h3&gt;Real-time crowd&amp;nbsp;control&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Nice research from Jeff Bigham's&amp;nbsp;group.&lt;/li&gt;
&lt;li&gt;Compares different strategies of merging input from different&amp;nbsp;users.&lt;/li&gt;
&lt;li&gt;Reminded me of my &lt;a href="/android-powered-mindstorms"&gt;twitter mindstorms project&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;iframe width="560" height="315" src="http://www.youtube.com/embed/P_Tqn-3BF_I" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;

&lt;h3&gt;Crowds in two&amp;nbsp;seconds&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Great research from MIT crowdsourcing folks (Michael Bernstein, Rob&amp;nbsp;Miller)&lt;/li&gt;
&lt;li&gt;Retainer: keep workers "on tap" - ready to work by paying&amp;nbsp;$0.30/hour.&lt;/li&gt;
&lt;li&gt;Rapid refinement: crowd algorithm to narrow search space to&amp;nbsp;accelerate.&lt;/li&gt;
&lt;li&gt;Cool application: take movies, crowdsource the best&amp;nbsp;moment.&lt;/li&gt;
&lt;/ul&gt;

&lt;iframe width="560" height="315" src="http://www.youtube.com/embed/9IICXFUP6MM" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;

&lt;h3&gt;The Jabberwocky programming&amp;nbsp;environment&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Really ambitious MTurk like&amp;nbsp;project.&lt;/li&gt;
&lt;li&gt;Combine different worker types from different spheres (social groups, paid workers,&amp;nbsp;machines)&lt;/li&gt;
&lt;li&gt;Full runtime stack (Dog high level language, ManReduce low level language, Dormouse&amp;nbsp;runtime)&lt;/li&gt;
&lt;li&gt;Great potential to mix different spheres of workers, but a rather intimidating project... scope&amp;nbsp;creep!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More in the &lt;a href="http://kamvar.org/assets/papers/jabberwocky.pdf"&gt;Jabberwocky&lt;/a&gt;&amp;nbsp;paper.&lt;/p&gt;

&lt;h3&gt;Real-time collaborative coding in a web&amp;nbsp;IDE&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;More awesome work from MIT (Max Goldman, Greg Little, Rob&amp;nbsp;Miller)&lt;/li&gt;
&lt;li&gt;Web-based Java EtherPad editor for&amp;nbsp;collaboration&lt;/li&gt;
&lt;li&gt;Main problem: others leave code in semi-working state. When to&amp;nbsp;sync?&lt;/li&gt;
&lt;li&gt;Idea: automatic error-aware integration. Auto-sync code when it compiles (or - tests&amp;nbsp;pass)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More on the &lt;a href="http://groups.csail.mit.edu/uid/collabode/"&gt;Collabode&lt;/a&gt;&amp;nbsp;site.&lt;/p&gt;

&lt;h1&gt;Mobile physical&amp;nbsp;computing&lt;/h1&gt;

&lt;p&gt;Interesting work in mobile virtual and projected UI&amp;nbsp;spaces.&lt;/p&gt;

&lt;h3&gt;PocketTouch&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Great work from Microsoft Research to let you use your phone through fabric (while in the&amp;nbsp;pocket)&lt;/li&gt;
&lt;li&gt;Since orientation in-pocket is unclear, there's an orientation setting gesture (not sold on&amp;nbsp;this)&lt;/li&gt;
&lt;li&gt;Unfortunately typical front pocket jeans work rather&amp;nbsp;poorly.&lt;/li&gt;
&lt;/ul&gt;

&lt;iframe width="560" height="315" src="http://www.youtube.com/embed/fHSDpE0kTag" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;

&lt;h3&gt;Multi-user Interaction with Handheld&amp;nbsp;Projectors&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Most projector-based systems require a fixed place. This one is fully&amp;nbsp;mobile.&lt;/li&gt;
&lt;li&gt;System produces visible projection (for image) and invisible projection (for&amp;nbsp;data)&lt;/li&gt;
&lt;li&gt;Camera tracks location of nearby&amp;nbsp;projections&lt;/li&gt;
&lt;li&gt;Cool applications: virtual boxing, content transfer, 3D model&amp;nbsp;viewing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="http://www.disneyresearch.com/_images/459-00B516CF.jpg" alt="concept" /&gt;&lt;/p&gt;

&lt;p&gt;To get a better sense of the project, take a look at &lt;a href="http://www.disneyresearch.com/research/projects/hci_sidebyside_drp.htm"&gt;this video&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Imaginary&amp;nbsp;phone&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Lets you control a mobile phone device without taking it out of your&amp;nbsp;pocket.&lt;/li&gt;
&lt;li&gt;Maps iPhone controls to the palm of your hand similar to &lt;a href="http://en.wikipedia.org/wiki/Guidonian_hand"&gt;Guidonian&amp;nbsp;hand&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Touchscreen progression: fingers replaced styli. Next step: palm replaces phone (bit of a&amp;nbsp;stretch)&lt;/li&gt;
&lt;/ul&gt;

&lt;iframe width="560" height="315" src="http://www.youtube.com/embed/aCARtauIS50" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;

&lt;h3&gt;OmniTouch&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Shoulder mounted depth&amp;nbsp;camera/projector.&lt;/li&gt;
&lt;li&gt;Works for all sorts of surfaces. Heavy math for tracking and&amp;nbsp;projecting.&lt;/li&gt;
&lt;li&gt;Tracks "hover" (haven't seen this notion before) and "touch"&amp;nbsp;states.&lt;/li&gt;
&lt;/ul&gt;

&lt;iframe width="560" height="315" src="http://www.youtube.com/embed/Pz17lbjOFn8" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;

&lt;h1&gt;Music&lt;/h1&gt;

&lt;p&gt;I had the pleasure of hearing &lt;a href="https://ccrma.stanford.edu/~ge/"&gt;Ge Wang&lt;/a&gt; speak
about some of his older projects, including &lt;a href="http://www.youtube.com/watch?v=RhCJq7EAJJA"&gt;Ocarina&lt;/a&gt;,
&lt;a href="http://www.youtube.com/watch?v=KetWJi0zou0"&gt;Leaf Trombone&lt;/a&gt;, the Stanford Mobile orchestra, and others. Notably I
hadn't really seen the &lt;a href="http://chuck.cs.princeton.edu/doc/language/"&gt;ChucK language in action&lt;/a&gt;, and would be
interested in seeing if the &lt;a href="http://www.html5rocks.com/en/tutorials/webaudio/intro/"&gt;Web Audio API&lt;/a&gt; could support this sort of thing.
Browser-based ChucK,&amp;nbsp;anyone?&lt;/p&gt;

&lt;p&gt;The last piece of research that I really enjoyed was called "onNote: Playing
printed music scores as a musical instrument". The idea here was to use OMR
techniques for markerless tracking of sheet music using positioning and using a
finger for pointing. The other neat application here was to support
compositional remixing by literally cutting up sheet music and splicing it back&amp;nbsp;together.&lt;/p&gt;

&lt;iframe width="420" height="315" src="http://www.youtube.com/embed/fGOk16Cnq7c" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;This was my first UIST, and though I'm unlikely to have new research to submit
for UIST 2012, I'm seriously considering going anyway, just to stay on top of
the great work that this vibrant community&amp;nbsp;generates.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/uist-2011</feedburner:origLink></entry><entry><title>Screen video capture for Chrome OS</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/uQFuxEfyY3Y/screen-capture-for-chrome-os" /><updated>2011-10-04T09:00:00Z</updated><published>2011-10-04T09:00:00Z</published><id>http://smus.com/screen-capture-for-chrome-os</id><content type="html">
      
&lt;h1&gt;
  Screen video capture for Chrome&amp;nbsp;OS
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Oct. 4, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Oct. 4, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;This post is about video capture in Chrome that doesn't rely on any
external dependencies like Flash (no fun), NPAPI (not supported on
Chrome OS) and Native Client (not &lt;em&gt;yet&lt;/em&gt; supported on Chrome&amp;nbsp;OS).&lt;/p&gt;

&lt;p&gt;I take screenshots all the time for bug reporting, image editing, etc.
On OS X, this functionality is conveniently built in, and available
through &lt;code&gt;Command&lt;/code&gt; - &lt;code&gt;Shift&lt;/code&gt; - &lt;code&gt;4&lt;/code&gt;. As a web denizen, I find it very
useful to auto-upload these captures to a remote server, so I wrote this
&lt;a href="https://github.com/borismus/screencapture-www"&gt;minimal image uploader&lt;/a&gt; which replaces the default behavior
on OS X to capture the screenshot, and also uploads it to a picture
hosting&amp;nbsp;service.&lt;/p&gt;

&lt;p&gt;Taking video capture of various UIs is also immensely useful for showing
demonstrations, complex interactions, and subtle bugs. I recently
re-discovered that QuickTime on OS X comes with this functionality built
in. Prior to that I used (paid) ScreenFlow, which also has very nice
dimension cropping and time dilation&amp;nbsp;features.&lt;/p&gt;

&lt;p&gt;What if we're on a web-only device, such as a Chromebook running Chrome
OS? There is a still screenshotting API, but capturing video is less
trivial. I've released an extension that captures and play backs video
captures inside Chrome, and also lets you share stills to Picasa (using
the OAuth 2 &lt;a href="http://smus.com/oauth2-chrome-extensions"&gt;extension library&lt;/a&gt;). It's available on the
&lt;a href="https://chrome.google.com/webstore/detail/omahgjnmfgeeeoekegajhndkncocoofd"&gt;webstore&lt;/a&gt;, and the source is on &lt;a href="https://github.com/borismus/chrome-screencast"&gt;github&lt;/a&gt;. Read on to
learn how it works, and see how you can&amp;nbsp;help.&lt;/p&gt;

&lt;h1&gt;Screenshots in&amp;nbsp;Chrome&lt;/h1&gt;

&lt;p&gt;Chrome provides the &lt;a href="http://code.google.com/chrome/extensions/tabs.html#method-captureVisibleTab"&gt;&lt;code&gt;captureVisibleTab&lt;/code&gt;&lt;/a&gt; extension API for taking
a screenshot of a tab. It requires host permissions on the page, but as
usual the &lt;all_urls&gt; permission will enable the API across all pages
(with some exceptions). A few successful extensions, such as
&lt;a href="http://awesomescreenshot.com/"&gt;Awesome Screenshot&lt;/a&gt;, use this API and allow
cropping, annotation and sharing of screen&amp;nbsp;grabs.&lt;/p&gt;

&lt;p&gt;What if you want to capture video of a tab? Chrome provides no
pre-existing API for this purpose, however, we can piggyback on the
still screenshot API, executing it repeatedly from the background page
for every frame we want to&amp;nbsp;capture:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var images = [];
var FPS = 30;
var QUALITY = 50;
timer = setInterval(function() {
  chrome.tabs.captureVisibleTab(null, {quality: QUALITY},
    function(img) {
      images.push(img);
    });
}, 1000 / FPS);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As we capture, we store the base64-encoded strings representing video
frames in an array. Once we're done capturing, we can simulate video
playback by rapidly swapping the images in and&amp;nbsp;out:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var background = chrome.extension.getBackgroundPage();
timer = setInterval(function() {
  if (currentIndex &amp;gt;= images.length - 1) {
    pause();
    return;
  }
  setIndex(currentIndex + 1);
  updateSliderPosition();
}, 1000 / background.FPS);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This approach turns out to be surprisingly efficient, with the extension
being able to capture at 30 FPS on a MacBook Air, and 10 FPS on a
Chromebook without too much noticeable&amp;nbsp;slowdown.&lt;/p&gt;

&lt;p&gt;Note that we rely on a fixed FPS for ease of implementation, however one
could imagine using &lt;code&gt;requestAnimationFrame&lt;/code&gt; and tracking the variable
frame rate so that the playback speed is reasonable. However, there are
definitely precision issues with JavaScript's timers, so this is a much
more challenging&amp;nbsp;approach.&lt;/p&gt;

&lt;p&gt;So we can capture and playback videos inside the browser, but getting it
out of the browser is another matter entirely. As a temporary measure,
my colleague &lt;a href="http://greenido.wordpress.com"&gt;Ido Green&lt;/a&gt; built a screen stitching service which
encodes multiple images into a movie using ffmpeg. Ideally, of course,
we would encode in the browser. Perhaps a JavaScript video encoder could
be implemented, though the performance may be too poor for practical
use. Alternatively, a ffmpeg Native Client-based approach might be
suitable, especially given that ffmpeg has &lt;a href="http://code.google.com/p/naclports/source/browse/trunk/src/libraries/ffmpeg-0.5/"&gt;already been ported&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;Free&amp;nbsp;ideas&lt;/h1&gt;

&lt;p&gt;There are a few logical next steps for this sample. As already
mentioned, encoding video in the browser is a top priority, but there
are a slew of other interesting directions, some of which can be seen as
features, and others as separate&amp;nbsp;products.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;captureVisibleTab&lt;/code&gt; API doesn't track the mouse cursor. This could
be done by injecting an overlay onto the current page and tracking
mousemove and click events. This data could then either be drawn onto
a canvas context, or encoded separately as &lt;code&gt;mouseData&lt;/code&gt;, and then drawn
with JavaScript at playback&amp;nbsp;time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cropping the video dimensions, modifying the video time schedule
(speedup, slowdown, truncation) and annotation are all desired
video-editing class features that could be implemented by treating
images as&amp;nbsp;canvases.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A compelling use case for this technology is creating screen sharing
sessions for demos and presentations. Thus, it would be very useful to
stream the video to a server, and broadcast it to multiple clients in
real time. &lt;a href="http://updates.html5rocks.com/2011/08/What-s-different-in-the-new-WebSocket-protocol"&gt;Binary websockets&lt;/a&gt; are now available in Chrome,
and this could be a great&amp;nbsp;application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Audio annotations on screen captures make perfect sense, and are
widely supported by desktop applications. APIs for sound capture have
been a &lt;a href="http://www.w3.org/2009/dap/"&gt;long time coming&lt;/a&gt;, but finally we may have an answer
via the &lt;a href="http://www.webrtc.org/"&gt;WebRTC&lt;/a&gt; ecosystem, and the &lt;code&gt;getUserMedia&lt;/code&gt; call.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the way, I've switched to exclusively using Markdown for all of my
published writing, and wrote an &lt;a href="https://github.com/borismus/markdown-preview"&gt;markdown preview&lt;/a&gt; for Chrome
to make my life a bit&amp;nbsp;easier.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/screen-capture-for-chrome-os</feedburner:origLink></entry><entry><title>Loading Large Assets in Modern HTML5 Games</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/p7xf8hR-k_M/game-asset-loader" /><updated>2011-09-22T15:00:36Z</updated><published>2011-09-22T15:00:36Z</published><id>http://smus.com/game-asset-loader</id><content type="html">
      
&lt;h1&gt;
  Loading Large Assets in Modern HTML5&amp;nbsp;Games
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Sept. 22, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Sept. 22, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;HTML5 games are here today, and rapidly increasing in complexity. Impressive
&lt;a href="http://madebyevan.com/webgl-water/"&gt;demos&lt;/a&gt; are &lt;a href="http://www.chromeexperiments.com/"&gt;everywhere&lt;/a&gt;, and prominent titles like &lt;a href="https://chrome.google.com/webstore/detail/ciamkmigckbgfajcieiflmkedohjjohh"&gt;Gun
Bros&lt;/a&gt; and &lt;a href="http://chrome.angrybirds.com"&gt;Angry Birds&lt;/a&gt; prove that it's possible to
create competitive gaming experiences in the browser. Games like these are
possible thanks largely to the modern web stack which includes WebGL, the Web
Audio API, Web Sockets and&amp;nbsp;others.&lt;/p&gt;

&lt;p&gt;Often forgotten, however, is the less sexy story of loading game assets. As the
web platform progresses and allows for increasingly complex games, game assets
(ex. textures, movies, music and images) grow in size and number, and asset
management becomes a sticking point for game&amp;nbsp;developers.&lt;/p&gt;

&lt;p&gt;Let me share with you some&amp;nbsp;truths:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Modern games require gigabytes of assets (textures, movies,&amp;nbsp;etc)&lt;/li&gt;
&lt;li&gt;Gamers don't like waiting for their game to&amp;nbsp;load&lt;/li&gt;
&lt;li&gt;Browser gamers want to be able to play regardless of internet&amp;nbsp;connectivity&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;span class="dquo"&gt;"&lt;/span&gt;But wait," you say, "I know! Just use the &lt;a href="http://diveintohtml5.org/offline.html"&gt;Application Cache&lt;/a&gt;
and yer done!". Not so fast, dear reader... As described below, there are
problems with this approach, and I propose some&amp;nbsp;solutions.&lt;/p&gt;

&lt;h1&gt;Problems with Application&amp;nbsp;Cache&lt;/h1&gt;

&lt;p&gt;So you've started implementing your awesome asset loading solution using
AppCache. The good news is that there are some useful tools to help you debug
if you have taken this difficult&amp;nbsp;route:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You can get basic information about the site's app cache through the
Developer Tools' &lt;a href="http://code.google.com/chrome/devtools/docs/resources.html"&gt;resource panel&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;You can view (and remove!) caches stored in Chrome by navigating to
&lt;code&gt;chrome://appcache-internals/&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But let me be blunt: &lt;strong&gt;AppCache is annoying to deal with&lt;/strong&gt;. If you've made a
small error in your cache manifest file, you'll quickly hit a brick
wall. I ran into an issue where I forgot to include a &lt;code&gt;NETWORK:&lt;/code&gt;
fallback clause, and wasted hours trying to figure out why all of my
XHRs were responding with status&amp;nbsp;0.&lt;/p&gt;

&lt;p&gt;Part of what makes AppCache difficult to debug is its very &lt;strong&gt;limited
JavaScript API&lt;/strong&gt;. Aside from letting you inspect the status of the entire cache
with &lt;code&gt;window.applicationCache&lt;/code&gt; and the &lt;code&gt;updateready&lt;/code&gt; event, AppCache doesn't
give us much to work with. There's no way to tell if a particular resource
we're dealing with is cached or not and no programmatic way of clearing the&amp;nbsp;cache.&lt;/p&gt;

&lt;p&gt;AppCache takes a fully transactional approach to asset loading.  Either
the cache is fully loaded, or fully unloaded. Compounding this issue,
it's impossible to resume the download of an AppCache. Thus, if you have
a large amount of assets, your user will have to &lt;strong&gt;wait a long time for
everything to be loaded&lt;/strong&gt;, and if they reload, they will need to restart
their cache&amp;nbsp;download.&lt;/p&gt;

&lt;p&gt;Lastly, you can only include one cache manifest per page, making it
&lt;strong&gt;impossible to group assets&lt;/strong&gt; into multiple bundles. There are hacks that
use multiple iframes with different cache manifests to work around this
limitation (used in &lt;a href="http://chrome.angrybirds.com"&gt;Angry Birds&lt;/a&gt;), but these are&amp;nbsp;ugly!&lt;/p&gt;

&lt;p&gt;Ultimately, what we need is a well-thought-out Application Cache
enhancement or replacement. Given how quickly web standards bodies move,
I've started thinking a bit about a transitional&amp;nbsp;solution.&lt;/p&gt;

&lt;h1&gt;Designing a game asset&amp;nbsp;loader&lt;/h1&gt;

&lt;p&gt;An ideal asset loading solution requires some of these&amp;nbsp;features:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Granular asset loading. Load all, in groups, or&amp;nbsp;individually.&lt;/li&gt;
&lt;li&gt;No asset size&amp;nbsp;limits.&lt;/li&gt;
&lt;li&gt;Offline&amp;nbsp;capability.&lt;/li&gt;
&lt;li&gt;Programatic control over&amp;nbsp;assets.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It makes sense to group assets in bundles and let the loader take care
of the details. We can even create a custom manifest format, for
example, in JSON&amp;nbsp;format:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  "assetRoot": "./media/",   // The root of the assets.
  "bundles": [{
    "name": "core",          // A bundle definition.
    "contents": [            // The contents within.
      "theme.mp3",
      "loading.jpg"
    ]
  }, {
    "name": "level1",        // Multiple bundles defined.
    "contents": [            // Note: order implicit since bundles
      "L1/background.jpg",   // objects are stored in an array.
      "L1/blip.wav"
    ]
  }, {
    "name": "level2",
    "contents": [
      "L2/intro.mov"
    ]
  }],
  "autoDownload": false      // If true, download all in order.
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;With this manifest format in mind, sample API usage might look like&amp;nbsp;this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// Load the asset library.
var gal = new GameAssetLoader('/path/to/gal.manifest');

// Read the manifest and other good stuff.
gal.init(function() {
  // When ready, download the bundle named 'core'.
  gal.download('core');
});

// When the core assets are loaded.
gal.onLoaded('core', function(result) {
  if (result.success) {
    // Show a loading indicator.
    document.querySelector('img').src = gal.get('loading.jpg');
  }
});

// Check the progress of the download.
gal.onProgress('core', function(status) {
  console.log('status:', status.current/status.total, '%');
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that although I've been using the name Game Asset Loader, this
approach can be used for loading any large non-game assets, such as for
example, a video or photo&amp;nbsp;gallery.&lt;/p&gt;

&lt;h1&gt;Implementation&amp;nbsp;details&lt;/h1&gt;

&lt;p&gt;Luckily, the modern web stack enables us to create a custom solution to
address all of these requirements. By leveraging technologies such as
the HTML5 Filesystem API or Indexed DB, we have programmatic access to
a storage mechanism that we can use to build an asset loader described&amp;nbsp;here.&lt;/p&gt;

&lt;p&gt;I used the &lt;a href="http://www.html5rocks.com/en/tutorials/file/filesystem/"&gt;Filesystem API&lt;/a&gt; to implement a version of the asset
loader. The code requests a large amount of persistent storage using the 
&lt;a href="https://groups.google.com/a/chromium.org/group/chromium-html5/msg/5261d24266ba4366?dmode=source"&gt;Quota API&lt;/a&gt;, which is undocumented, but works&amp;nbsp;anyway:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// Get quota.
storageInfo.requestQuota(window.PERSISTENT, quota,
  onQuotaGranted, onError);

// Callback when the quota API has granted quota
function onQuotaGranted = function(grantedBytes) {
  // Save grantedBytes in the adapter
  that.grantedBytes = grantedBytes;
  // Once quota is grantedBytes, initialize a filesystem
  requestFileSystem(window.PERSISTENT, grantedBytes, onInitFS, onError);
};

// Callback when the filesystem API has created a filesystem.
function onInitFS = function(fs) {
  // Create a directory for the root of the assets.
  fs.root.getDirectory(ROOT_DIR, {create: true}, function(dirEntry) {
    that.root = dirEntry;
  }, onError);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The approach fetches assets with &lt;code&gt;XMLHttpRequest&lt;/code&gt;, and stores them in the
filesystem. All files in the filesystem are accessible via the &lt;code&gt;filesystem://&lt;/code&gt;
schema, and can be used as any other resource. This filesystem URL is returned
by the library in the &lt;code&gt;get(path)&lt;/code&gt; call.&lt;/p&gt;

&lt;p&gt;Note that the writable HTML5 filesystem API is currently available in Chrome
only, but that it's quite possible to use IndexedDB (supported in Firefox and
IE10) as the data&amp;nbsp;store.&lt;/p&gt;

&lt;h1&gt;Usage&amp;nbsp;scenarios&lt;/h1&gt;

&lt;p&gt;The following section briefly describes what the game asset loader (GAL) does
in several&amp;nbsp;scenarios.&lt;/p&gt;

&lt;p&gt;Player goes to game.com which uses the game asset loader. The game calls
&lt;code&gt;gal.download('core')&lt;/code&gt; to download core assets and
&lt;code&gt;gal.download('level1')&lt;/code&gt; to load the first level into the player’s
filesystem. While the core bundle loads, the game displays a loading
indicator. Once core is loaded, the game displays the main menu. As soon
as the first level is loaded, the "Play now" button is enabled. As the
player plays, the GAL downloads more of the levels in the&amp;nbsp;background.&lt;/p&gt;

&lt;p&gt;Next time, the player tries playing offline. He goes to game.com, whose
code is cached via AppCache, and loads GAL again. This time GAL knows it’s
offline, looks up its manifest stored on the filesystem and doesn’t try to
download new assets. The old assets still work&amp;nbsp;though.&lt;/p&gt;

&lt;p&gt;Player is still offline, making good progress, and beats level 5, but
there are no assets downloaded for level 6. Luckily, before starting
each level, the game calls &lt;code&gt;gal.download('levelBundle')&lt;/code&gt; to make sure
that the contents of that bundle are downloaded. The callback returns an
error and the game displays an error telling the player that he needs to
be online to download the next&amp;nbsp;level.&lt;/p&gt;

&lt;p&gt;So the player goes online and tries again. GAL re-downloads a manifest.
Next, GAL tries re-downloading every asset that the JS requests. Luckily most
of these assets are still in the browser cache, and won't be re-downloaded. The
loader then saves all of the assets in the filesystem, clobbering old files
indiscriminately. (This is bad, and needs to be fixed. Read&amp;nbsp;on!)&lt;/p&gt;

&lt;h1&gt;Future&amp;nbsp;work&lt;/h1&gt;

&lt;p&gt;In particular, re-downloading every asset while online is not desirable
behavior, and we can't always rely on the browser cache for this. For
smaller files, we can probably rely on ETag and Last-Modified headers
and hopefully the browser won't re-download the files. However, the
&lt;strong&gt;asset loader will still overwrite the asset in the filesystem, even if
it's unmodified&lt;/strong&gt;. This needs to be fixed. Large files are not likely to be
cached by the browser, so we will need more intelligent &lt;strong&gt;caching built into
the asset loader itself&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There are other edge cases that need to be considered, such as what happens
when an &lt;strong&gt;asset is removed from a manifest&lt;/strong&gt;. Ideally if this occurs, it
&lt;strong&gt;should also be removed from the filesystem&lt;/strong&gt;, but this is not currently&amp;nbsp;implemented.&lt;/p&gt;

&lt;p&gt;I'm happy to release the &lt;a href="https://github.com/borismus/game-asset-loader"&gt;source&lt;/a&gt; under the permissive Apache 2
license and provide &lt;a href="https://github.com/borismus/game-asset-loader/blob/master/tests/tests.js"&gt;unit tests&lt;/a&gt; and a &lt;a href="https://github.com/borismus/game-asset-loader/tree/master/tests/game"&gt;sample&lt;/a&gt; project
for your perusal. It's well documented and should be reasonably easy to
understand. I've also made provisions to separate the core library
interface from the Filesystem-based implementation, making it even
easier to implement an Indexed DB&amp;nbsp;adapter.&lt;/p&gt;

&lt;p&gt;Before I go, let me reiterate that this library isn't quite production ready,
but a step in the right direction for facilitating real games on the web.
Please comment below if you have feedback on the idea, or are using the
library to write a game of your&amp;nbsp;own!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/game-asset-loader</feedburner:origLink></entry><entry><title>Developing Multiplayer HTML5 Games with Node.js</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/TGMLxGKH5UI/multiplayer-html5-games-with-node" /><updated>2011-08-30T09:00:36Z</updated><published>2011-08-30T09:00:36Z</published><id>http://smus.com/multiplayer-html5-games-with-node</id><content type="html">
      
&lt;h1&gt;
  Developing Multiplayer HTML5 Games with&amp;nbsp;Node.js
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Aug. 30, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Aug. 30, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;One day I had some friends over at my house introducing me some cool iPad
games. One of the games was Osmos, developed by an Canadian indie studio called
&lt;a href="http://www.hemispheregames.com"&gt;Hemisphere Games&lt;/a&gt;. You control a little blob that floats in 2D space, and
the only thing your blob can do is shoot pieces of itself in a given
direction, which propels it in the opposite direction. The rules of the game
are simple, the main rule being that when two blobs collide, the larger one
will consume the smaller one. The rest of the rules pretty much follow directly
from conservation of mass and energy. &lt;a href="http://www.youtube.com/watch?v=pso6UBicLWU"&gt;See for yourself&lt;/a&gt; - it's way
better than it&amp;nbsp;sounds!&lt;/p&gt;

&lt;p&gt;Osmos really caught my attention because of its simple but engaging gameplay,
meditative pace and distinct &lt;strong&gt;lack of multiplayer support&lt;/strong&gt;, which struck me
as a potentially very interesting problem to tackle. And so, &lt;a href="http://o.smus.com/"&gt;osMUs&lt;/a&gt; (mu for
multiplayer) was born as a browser-based multiplayer Osmos&amp;nbsp;clone.&lt;/p&gt;

&lt;h2&gt;How it&amp;nbsp;works&lt;/h2&gt;

&lt;p&gt;When a browser navigates to the osmus landing page, the server sends the new
client the current state of its universe, which is composed of blobs with
randomized velocities. At this point, the client can passively watch the game
progress, but of course, can also join the game as a player controlled blob.
Once a player joins, he can click or tap (on mobile devices) the canvas to
shoot off a new&amp;nbsp;blob.&lt;/p&gt;

&lt;p&gt;As the game progresses, the server decides when someone (possibly one of the
autonomous blobs) is victorious, at which point, players are notified and the
game is&amp;nbsp;restarted.&lt;/p&gt;

&lt;p&gt;The rest of this post is about some development-related details So if you just
want to try it out, &lt;a href="http://o.smus.com/"&gt;go ahead&lt;/a&gt;. Note, however, that Osmus works on
Chrome Stable (version 13) and&amp;nbsp;iPad.&lt;/p&gt;

&lt;h2&gt;Game&amp;nbsp;architecture&lt;/h2&gt;

&lt;p&gt;I wrote osmus to be split into distinct, loosely coupled components both to
make the codebase more approachable for other contributors, and to make it easy
to experiment with interchangeable&amp;nbsp;technologies.&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2011/osmus-architecture.png" alt="architecture" /&gt;&lt;/p&gt;

&lt;p&gt;Osmus uses a shared game engine that runs in both the browser and on
the server. The engine is a simple state machine whose primary function
is to compute the next game state as a function of time using the rules of
physics defined&amp;nbsp;within.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Game.prototype.computeState = function(delta) {
  var newState = {};
  // Compute a bunch of stuff based on this.state
  return newState;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is a pretty narrow definition of a &lt;em&gt;Game Engine&lt;/em&gt;. In the game developer
world, what's typically meant by a game engine may include anything from a
renderer, sound player, networking layer, &lt;a href="http://en.wikipedia.org/wiki/Game_engine"&gt;etc&lt;/a&gt;. In this case, I've
made very clear divisions between these components, and the osmus game core
only includes just the physical state machine, so that both client and server
can compute the next states and be reasonably synchronized in&amp;nbsp;time.&lt;/p&gt;

&lt;p&gt;The client is composed of three main components: a renderer, input
manager and sound manager. I built a very simple canvas-based renderer
that draws blobs as red circles, and player blobs are green ones. My
colleague &lt;a href="http://twitter.com/kurrik"&gt;Arne Roomann-Kurrik&lt;/a&gt; wrote an alternative
&lt;a href="https://github.com/mrdoob/three.js/"&gt;three.js&lt;/a&gt; based renderer with some epic shaders and&amp;nbsp;shadows.&lt;/p&gt;

&lt;p&gt;The sound manager handles playback of both sound effects and background music
(taken from &lt;a href="http://feryl.bandcamp.com/album/8-bit-magic-a-module-chiptune-collection"&gt;8-bit Magic&lt;/a&gt;). The current implementation uses audio tags,
with two &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; elements, one for the background music channel, and one for
the sound effects. There are known limitations of this approach, but given the
modularity of my implementation, the sound implementation can be swapped out
for one that uses Chrome's &lt;a href="https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html"&gt;Web Audio API&lt;/a&gt;, for&amp;nbsp;example.&lt;/p&gt;

&lt;p&gt;Finally, the input manager handles mouse events, but can be replaced with one
that uses touch instead, for a mobile version. In the mobile context, it will
likely make sense to use CSS3 transformations instead of canvas, since CSS3 is
hardware accelerated on iOS, while HTML5 canvas still isn't, and WebGL is not&amp;nbsp;implemented.&lt;/p&gt;

&lt;p&gt;Speaking of mobile, I was happily surprised that osmus works pretty well on
iPad, especially on an iPad 2 running the latest iOS version. This is really
great, and one of the tangible benefits of writing games for the open&amp;nbsp;web.&lt;/p&gt;

&lt;h2&gt;Networking is&amp;nbsp;hard&lt;/h2&gt;

&lt;p&gt;From a networking perspective, a game is a rather ambitious project that
requires seamless real-time synchronization between clients. Because of this,
bidirectional client-server communication is essential. In the modern web
stack, this is provided by &lt;a href="http://dev.w3.org/html5/websockets/"&gt;Web Sockets&lt;/a&gt; which supply a thin layer
above TCP and hide a lot of gory details from the implementer. To further hide
network stack details, I use the &lt;a href="http://socket.io/"&gt;socket.io&lt;/a&gt; library, which provides a dead
simple event-driven abstraction for the whole thing. Unfortunately there's
currently no support for binary data, which would greatly compress message
size, perhaps by an order of two magnitudes in the case of&amp;nbsp;osmos.&lt;/p&gt;

&lt;p&gt;From a bit of research which included this &lt;a href="http://www.youtube.com/watch?v=zj1qTrpuXJ8"&gt;nice talk from Rob Hawkes&lt;/a&gt;, it
became clear that to have any sort of shared experience, the simplest model is
to have the true game state on the server, and have clients periodically sync
with it. The main trade off here is synchronization quality vs. network
traffic&amp;nbsp;required.&lt;/p&gt;

&lt;p&gt;On one extreme, a game can be written by having the game logic entirely on the
server and sending updates (or perhaps even &lt;a href="http://www.onlive.com/"&gt;simply screenshots&lt;/a&gt;) to
the client at 60 FPS, but this is generally not feasible due to the sheer
amount of bandwidth required for this model. On the opposite extreme, you can
imagine a network architecture in which clients connect, get initial state, and
are then largely&amp;nbsp;autonomous.&lt;/p&gt;

&lt;p&gt;In practice, there is a happy medium in which many multiplayer games fall,
which means replicating non-trivial code in both the client and the server.
Luckily, now that we're in the ubiquitous JavaScript era, there is no longer a
need to duplicate functionality, but can instead share code by writing the game
engine in JavaScript, and then running it in both a browser on the client, and
in &lt;a href="http://nodejs.org/"&gt;node.js&lt;/a&gt; on the&amp;nbsp;server.&lt;/p&gt;

&lt;p&gt;There's a lot more to be written about writing the multiplayer bits of osmus,
which will hopefully turn into a more detailed article at some point in the&amp;nbsp;future.&lt;/p&gt;

&lt;h2&gt;Shared JS&amp;nbsp;modules&lt;/h2&gt;

&lt;p&gt;As mentioned earlier, osmus uses a physics engine that's shared between clients
and the server. One might imagine that sharing JavaScript code between the two
would be a breeze, but it's not that&amp;nbsp;simple.&lt;/p&gt;

&lt;p&gt;Module loaders are a mess. There's the &lt;a href="http://www.commonjs.org/"&gt;CommonJS spec&lt;/a&gt;,
&lt;a href="http://requirejs.org/"&gt;RequireJS library&lt;/a&gt; and node.js require system, none of which play
nicely together. If you want to share code between client and server (one of
the big wins of JS on the server) without a module loader, you can use this
somewhat hacky&amp;nbsp;pattern:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function(exports) {

var MyClass = function() { /* ... */ };
var myObject = {};

exports.MyClass = MyClass;
exports.myObject = MyObject;

})(typeof global === "undefined" ? window : exports);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This hack relies on the fact that node.js defines a &lt;code&gt;global&lt;/code&gt; object while the
browser does not. With the hack, node.js &lt;code&gt;require()&lt;/code&gt; will be happy, and you can
also include the file in a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag without polluting your namespace,
assuming of course, that no other JS pollutes your namespace with a
&lt;code&gt;window.global&lt;/code&gt; object!&lt;/p&gt;

&lt;p&gt;Unfortunately this approach only works well for one shared module. As soon as
you have multiple modules depending on each other (via &lt;code&gt;require&lt;/code&gt;s in node-land,
and globals in browser-land), the difference between node's namespacing and
browser's inclusion becomes painfully apparent and requires more hacky&amp;nbsp;workarounds.&lt;/p&gt;

&lt;p&gt;Another approach is to use &lt;a href="http://substack.net/posts/24ab8c/browserify-browser-side-require-for-your-node-js"&gt;browserify&lt;/a&gt; to bundle all JS and emulate
requires in the browser. This approach relies on node.js to serve the
generated JS, which is not ideal, since static files should be served by a
&lt;a href="http://nginx.net/"&gt;webserver&lt;/a&gt; optimized for the purpose. However node.js + browserify can
be configured to compile JS that can be served statically without relying on
node to serve it. This approach introduces some&amp;nbsp;overhead:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Extra build step for&amp;nbsp;deploying.&lt;/li&gt;
&lt;li&gt;Performance overhead of whatever mechanism browserify uses to support
&lt;code&gt;require()&lt;/code&gt; calls.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Overall this approach sounds better to me, and I hope to try it out in a future
version of&amp;nbsp;osmus.&lt;/p&gt;

&lt;h2&gt;Your&amp;nbsp;turn&lt;/h2&gt;

&lt;p&gt;Today I'm releasing &lt;a href="http://o.smus.com/"&gt;osmus&lt;/a&gt; as a completely open source HTML5 game. Feel
free to &lt;a href="https://github.com/borismus/osmus"&gt;fork it&lt;/a&gt; to your heart's content. Oh, and for other game related
goodness, check out this article on &lt;a href="http://www.html5rocks.com/en/tutorials/canvas/performance/"&gt;HTML5 canvas performance&lt;/a&gt; recently
posted on&amp;nbsp;html5rocks.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/multiplayer-html5-games-with-node</feedburner:origLink></entry><entry><title>Global Chrome Media Keys with Key Socket</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/D1WN-OVQSh4/chrome-media-keys-revisited" /><updated>2011-07-28T09:00:35Z</updated><published>2011-07-28T09:00:35Z</published><id>http://smus.com/chrome-media-keys-revisited</id><content type="html">
      
&lt;h1&gt;
  Global Chrome Media Keys with Key&amp;nbsp;Socket
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="July 28, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;July 28, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;I just skipped to the next Google Music track without leaving vim. Wanna play?
Here's the &lt;a href="https://github.com/downloads/borismus/keysocket/KeySocket.zip"&gt;app&lt;/a&gt; and &lt;a href="https://chrome.google.com/webstore/detail/fphfgdknbpakeedbaenojjdcdoajihik"&gt;Chrome extension&lt;/a&gt;. To learn how it works, read&amp;nbsp;on!&lt;/p&gt;

&lt;h2&gt;Script injection&amp;nbsp;limitations&lt;/h2&gt;

&lt;p&gt;&lt;a href="/chrome-media-keys"&gt;Last time around&lt;/a&gt; I implemented keyboard bindings by injecting a content
script into every tab in Chrome, capturing key events and sending them to a
background page. This approach has some serious performance&amp;nbsp;drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Content scripts injected into each&amp;nbsp;page.&lt;/li&gt;
&lt;li&gt;Background pages don't perform very&amp;nbsp;well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And functional&amp;nbsp;limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Won't work on special URLs like &lt;code&gt;chrome://&lt;/code&gt; and &lt;code&gt;file://&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Won't work when the omnibox is&amp;nbsp;focused.&lt;/li&gt;
&lt;li&gt;Requires chrome to be in the&amp;nbsp;foreground.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Global key bindings and&amp;nbsp;websockets&lt;/h2&gt;

&lt;p&gt;What we really want is global key bindings. I don't care where my keyboard
focus happens to be right now, I just want to switch to the next freaking song!
This sort of thing requires OS-level event capture, which is functionality most
browsers don't come with. To get around this, I run a standalone app to capture
global keys and run a websocket server to send these events to the browser.
Note that this approach generalizes well to other use cases where functionality
is not available in a browser, but can be more readily implemented&amp;nbsp;natively.&lt;/p&gt;

&lt;p&gt;The obvious drawback to this approach is that it requires the user to run a
separate process to capture&amp;nbsp;events.&lt;/p&gt;

&lt;h2&gt;Media key bindings in Cocoa and&amp;nbsp;Python&lt;/h2&gt;

&lt;p&gt;Rogue Amoeba, maker of some popular OS X audio utilities, has a
&lt;a href="http://rogueamoeba.com/utm/2007/09/29/apple-keyboard-media-key-event-handling/"&gt;nice post&lt;/a&gt; on their blog on capturing media keys from an OS X
application. The basic idea is to subclass NSApplication and override the
sendEvent:&amp;nbsp;selector:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- (void)sendEvent: (NSEvent*)event {
  if( [event type] == NSSystemDefined &amp;amp;&amp;amp; [event subtype] == 8 ) {
      // Event processing
  }
  [super sendEvent: event];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which in PyObjC results in the following equivalent&amp;nbsp;code:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def sendEvent_(self, event):
    if event.type() is NSSystemDefined and event.subtype() is 8:
        # Event processing

    NSApplication.sendEvent_(self, event)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It's pretty neat to be able to implement Cocoa apps without having to write a
single line of objective C. Writing a statusbar app with no dock item was
surprisingly simple (though I have doubts that this works well for Lions and
Tigers and Bears). All that's required is to set &lt;code&gt;LSUIElement&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; in the&amp;nbsp;Info.plist.&lt;/p&gt;

&lt;p&gt;To package the whole PyObjC application, I wrote a setup.py script and used
&lt;a href="http://svn.pythonmac.org/py2app/py2app/trunk/doc/index.html"&gt;py2app&lt;/a&gt;, which generates a Mac OS X .app bundle which, from a user's
perspective is indistinguishable from an OS X app written in Objective&amp;nbsp;C.&lt;/p&gt;

&lt;h2&gt;A WebSocket server in&amp;nbsp;python&lt;/h2&gt;

&lt;p&gt;In addition to spawning off a Cocoa application and capturing events, of course
I create a WebSocket server. WebSockets use a pretty simple protocol which can
easily be implemented using python sockets. I based my implementation heavily
on &lt;a href="https://gist.github.com/512987"&gt;this one&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since a Cocoa application runs its own event loop which captures the
main thread, the websocket listener needs to run in a separate&amp;nbsp;thread:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class KeySocketServer(Thread):
    def __init__(self):
        self.server = websocket.WebSocketServer('localhost', 1337, KeySocket)
        Thread.__init__(self)

    def run(self):
        self.server.listen()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The WebSocket standards are still evolving and implementers are, as ever,
scrambling to catch up. The good news is that this means
&lt;a href="http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-06#section-4.6"&gt;binary support&lt;/a&gt; is coming, which is a boon for games and other
intensive network consumers. The bad news is that the latest Chrome canary (at
the time of writing), requires the response to contain the
&lt;code&gt;Sec-WebSocket-Accept&lt;/code&gt; header, conforming to the
&lt;a href="http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-06"&gt;draft-ietf-hybi-thewebsocketprotocol-06&lt;/a&gt;, which is incompatible with
the python WebSocket code I'm currently&amp;nbsp;using.&lt;/p&gt;

&lt;p&gt;On my wishlist is a robus python WebSockets implementation that supports
multiple versions of the spec while it's still in&amp;nbsp;flux.&lt;/p&gt;

&lt;h2&gt;Injected&amp;nbsp;scripts&lt;/h2&gt;

&lt;p&gt;On the Chrome extension side, a script is injected into the web player
application, which creates a WebSocket client that connects to the python
server on port 1337. When media keys are pressed, the python server sends
messages to the JS clients and the injected JS simulates DOM events in the web
player application, controlling music&amp;nbsp;playback.&lt;/p&gt;

&lt;h2&gt;Try it&amp;nbsp;out&lt;/h2&gt;

&lt;p&gt;If you listen to Google Music, thesixtyone or Grooveshark in Chrome on OS X and
want global key bindings, please install the &lt;a href="https://chrome.google.com/webstore/detail/fphfgdknbpakeedbaenojjdcdoajihik"&gt;extension&lt;/a&gt; and
&lt;a href="https://github.com/downloads/borismus/keysocket/KeySocket.zip"&gt;application&lt;/a&gt;. If you're feeling generous, contribute your time and love!
I'd gladly take fixes&amp;nbsp;for&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Key Socket servers for Linux and&amp;nbsp;Windows&lt;/li&gt;
&lt;li&gt;Content scripts to control other web audio&amp;nbsp;players&lt;/li&gt;
&lt;li&gt;Web Socket implementations that work with the new&amp;nbsp;spec&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And of course, here's the &lt;a href="https://github.com/borismus/keysocket"&gt;source&lt;/a&gt;.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/chrome-media-keys-revisited</feedburner:origLink></entry><entry><title>Extending Chrome Developer Tools</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/AaQRM-NCccs/extending-chrome-developer-tools" /><updated>2011-07-11T15:00:00Z</updated><published>2011-07-11T15:00:00Z</published><id>http://smus.com/extending-chrome-developer-tools</id><content type="html">
      
&lt;h1&gt;
  Extending Chrome Developer&amp;nbsp;Tools
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="July 11, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;July 11, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Chrome’s developer tools (also known as the WebKit inspector) are super useful
for web developers. If you aren't ramped up already, take a look at this
&lt;a href="/devtools-cheatsheet"&gt;cheat sheet overview&lt;/a&gt;. Also check out some of many online resources,
such as the &lt;a href="http://code.google.com/chrome/devtools/docs/overview.html"&gt;official documentation&lt;/a&gt;, which is quite readable, and this
&lt;a href="http://www.youtube.com/watch?v=nOEw9iiopwI"&gt;12 tricks screencast&lt;/a&gt; from Paul&amp;nbsp;Irish.&lt;/p&gt;

&lt;p&gt;But let’s not get sidetracked here. I wasn't going to write about using the
developer tools, it’s about &lt;strong&gt;developing&lt;/strong&gt; developer tools. There are two ways
to&amp;nbsp;go:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;You can extend the tools by writing chrome extensions that use the new
&lt;code&gt;chrome.experimental.devtools&lt;/code&gt; APIs. These recently created APIs are still
subject to change, and marked experimental, so it’s a bit more difficult to
distribute and install them compared to other chrome extension APIs (more on
this&amp;nbsp;later).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can quite easily hack the devtools code yourself, to customize it to
your needs. Then, if you do a really good job, you can contribute your changes
back to the chromium project and feel better for being such an awesome&amp;nbsp;person.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s dive in, starting from the&amp;nbsp;beginning.&lt;/p&gt;

&lt;h2&gt;Developer tools&amp;nbsp;extensions&lt;/h2&gt;

&lt;p&gt;Chrome provides &lt;a href="http://code.google.com/chrome/extensions/trunk/experimental.devtools.html"&gt;three separate APIs&lt;/a&gt; for extending the developer&amp;nbsp;tools:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Audits - add a new audit to the Audits tab in the developer tools:
&lt;img src="/media/images/blog/2011/audit.png" alt="auditshot" /&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Panels - add a whole new panel to the developer tools: &lt;img src="/media/images/blog/2011/panel.png" alt="panelshot" /&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Resources - access a &lt;a href="http://groups.google.com/group/http-archive-specification/web/har-1-2-spec"&gt;HAR file&lt;/a&gt; containing known&amp;nbsp;resources&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are several &lt;a href="http://code.google.com/chrome/extensions/trunk/samples.html#devtools"&gt;extension samples&lt;/a&gt; available as starting points,
which cover all of the devtools extension API. The API is geared towards
Audit-oriented extensions (HTML/CSS/Javascript validators, performance
analyzers) and Panel-oriented extensions (framework specific tooling, etc).
You can also write extensions that integrate more loosely with the developer
tools (perhaps only using the resources&amp;nbsp;API).&lt;/p&gt;

&lt;h3&gt;My JSHint audit&amp;nbsp;extension&lt;/h3&gt;

&lt;p&gt;I wanted to get my hands dirty, and wrote a &lt;a href="http://jshint.com"&gt;JSHint&lt;/a&gt;-based Javascript
validator extension. This extension uses the devtools audit API to create an
audit that checks all scripts (linked and inline) on the current page. Errors
are shown per-script in the audit results view. I wanted to go further, and
allow the user to click on the error and jump to the resource, but this wasn’t
possible with the developer tools extension API. To get around this limitation,
I patched the developer tools with a pretty quick fix. More on modifying the
devtools themselves&amp;nbsp;later.&lt;/p&gt;

&lt;p&gt;Check out the JSHint audit extension &lt;a href="https://github.com/borismus/jshint-extension"&gt;on github&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Writing an&amp;nbsp;extension&lt;/h3&gt;

&lt;p&gt;The first thing you should know about developer tools extensions, is that (at
the time of writing), they are experimental. This means that you will need to
launch chrome with a special flag to use them. I use a shell script called
&lt;code&gt;chrome-devtools&lt;/code&gt; in my &lt;code&gt;~/bin&lt;/code&gt; (which is in my &lt;code&gt;$PATH&lt;/code&gt; of&amp;nbsp;course):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary \
  --user-data-dir=/Users/smus/.chrome-devtools \
  --enable-experimental-extension-apis \
  --debug-devtools-frontend=/Users/smus/devtools_frontend
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let me explain these&amp;nbsp;switches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;--user-data-dir&lt;/code&gt;: specifies a custom profile for your chrome (so you have a
fresh profile to deal with and don’t accidentally clobber something important
in your main chrome&amp;nbsp;profile).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;--enable-experimental-extension-apis&lt;/code&gt;: turns on experimental extension&amp;nbsp;APIs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;More on the last switch&amp;nbsp;later.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the way, all of chrome’s switches are explained on &lt;a href="http://peter.sh/experiments/chromium-command-line-switches/"&gt;this page&lt;/a&gt;,
provided by Peter&amp;nbsp;Beverloo.&lt;/p&gt;

&lt;p&gt;Developer tools extensions are based on a devtools page, which gets loaded when
the devtools open. You can specify this page in the manifest like other pages
(such as background and options&amp;nbsp;pages):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  // manifest start
  "devtools_page": "devtools.html",
  // manifest end
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The API favors extensions that are built around an Audit or a Panel, and you
are free to use the resources API, as well as the rest of the chrome extension&amp;nbsp;features.&lt;/p&gt;

&lt;p&gt;One important note is that the devtools page has very limited access to chrome
extension APIs, so you need to use &lt;a href="http://code.google.com/chrome/extensions/messaging.html"&gt;messaging&lt;/a&gt; and a background page to
access the full chrome&amp;nbsp;API.&lt;/p&gt;

&lt;p&gt;devtools.html:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// Send request to the background page
chrome.extension.sendRequest({}, function(response) {
  // Handle response
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;background.html:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;chrome.extension.onRequest.addListener(function(request, sender, callback) {
  // Call some chrome extension APIs. For example,
  chrome.tabs.getSelected(null, function(tab) {
    // Etc... and then callback to devtools.html
    callback({data: tab.id});
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Debugging devtools&amp;nbsp;extensions&lt;/h3&gt;

&lt;p&gt;When writing Chrome extensions, you have the power of the Chrome developer
tools at your disposal. You can debug content scripts by inspecting the page
into which the Javascript has been injected. Background pages can be viewed by
running &lt;code&gt;chrome://extensions&lt;/code&gt; in developer mode, and clicking on
background.html for your&amp;nbsp;extension.&lt;/p&gt;

&lt;p&gt;However, when you write a developer tools extension, you rely on this
devtools_page as well as the rest of the extension ecosystem. Debugging this
page can get a bit meta – just inspect the devtools with the&amp;nbsp;devtools!&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2011/meta.png" alt="meta" /&gt;&lt;/p&gt;

&lt;p&gt;Now you can inspect your devtools.html page, and debug&amp;nbsp;away!&lt;/p&gt;

&lt;h2&gt;Changing the product&amp;nbsp;itself&lt;/h2&gt;

&lt;p&gt;In addition to being highly extensible via Chrome extensions, the devtools are
also pretty easy to modify and tinker with. As you noticed earlier, the tools
are a web application written in Javascript, CSS and HTML, and thus inspectable
by the devtools themselves. This web application is hidden inside Chrome, but
luckily Chrome makes it easy to run a custom version of it. Basically, it takes
three&amp;nbsp;steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download a &lt;a href="http://commondatastorage.googleapis.com/chromium-browser-continuous/index.html"&gt;devtools frontend zip&lt;/a&gt; (pre-packed version of
devtools). You will need to drill down into a directory for
&lt;a href="http://commondatastorage.googleapis.com/chromium-browser-continuous/index.html?path=Mac/91508/"&gt;your platform&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Extract the zip to some directory, &lt;code&gt;DIR&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Invoke Chrome with the &lt;code&gt;--debug-devtools-frontend=DIR&lt;/code&gt; switch, specifying
the same directory as in the previous&amp;nbsp;step.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since the &lt;code&gt;devtools_frontend.zip&lt;/code&gt; has some dependencies on Chrome, your zip file
and chrome version should be pretty close, or you may run into problems. You can
run the exact same version by also downloading the &lt;code&gt;chrome-platform.zip&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note: the Chromium project has a more detailed &lt;a href="http://code.google.com/chrome/devtools/docs/contributing.html"&gt;instruction set&lt;/a&gt; for
getting&amp;nbsp;started.&lt;/p&gt;

&lt;h3&gt;Making it your&amp;nbsp;own&lt;/h3&gt;

&lt;p&gt;Now that you have your custom devtools frontend, you can make tweaks to the
JS/CSS/HTML source, and see those changes in your development version of
chrome after you manually restart the&amp;nbsp;browser.&lt;/p&gt;

&lt;p&gt;For example, a bunch of people wanted to get rid of the yellow highlight that
appears overlaid in the browser window when hovering over DOM elements. At the
time of writing, the method responsible for this is in &lt;code&gt;inspector.js&lt;/code&gt; called
&lt;code&gt;WebInspector.highlightDOMNode&lt;/code&gt;. By applying this small patch, you can disable
the default&amp;nbsp;behavior.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1150,1151c1150
&amp;lt;     // Do not highlight the DOM node.
&amp;lt;     //this.highlightDOMNodeForTwoSeconds(nodeId);
---
&amp;gt;     this.highlightDOMNodeForTwoSeconds(nodeId);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can also easily make cosmetic tweaks to the devtools by changing the CSS.
A lot of the base styles are implemented in &lt;code&gt;inspector.css&lt;/code&gt;. For instance, for
presentation purposes, the devtools are a bit small, so I find it useful to
increase the size of the devtools, which is a one line CSS&amp;nbsp;change.&lt;/p&gt;

&lt;p&gt;There's already a ton of useful features packed into the devtools, but I bet
you can come up with some more awesomeness to add as an extension or chromium
patch. Let me know if you&amp;nbsp;do!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/extending-chrome-developer-tools</feedburner:origLink></entry><entry><title>OAuth 2.0 from Chrome Extensions</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/JIjLX_Fsf58/oauth2-chrome-extensions" /><updated>2011-07-07T09:00:00Z</updated><published>2011-07-07T09:00:00Z</published><id>http://smus.com/oauth2-chrome-extensions</id><content type="html">
      
&lt;h1&gt;
  OAuth 2.0 from Chrome&amp;nbsp;Extensions
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="July 7, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;July 7, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Applications that access online services often need to access a user's private
data. Chrome Extensions are no different. OAuth has emerged as the standard way
of letting users share their private resources across sites without having to
hand out their usernames and passwords. There is already a very nice 
&lt;a href="http://code.google.com/chrome/extensions/tut_oauth.html"&gt;OAuth library for Chrome Extensions&lt;/a&gt; that aims to simplify some of 
the pains that developers face when authorizing against OAuth&amp;nbsp;endpoints.&lt;/p&gt;

&lt;p&gt;Since this library was written, the OAuth standard enjoyed a version bump
(&lt;a href="http://oauth.net/2/"&gt;OAuth 2.0&lt;/a&gt;) which greatly simplifies the flow by no longer requiring
cryptography in the client. Also, some adventurous companies (notably
Google, Facebook and others) have actually implemented OAuth 2.0
endpoints. At the time of writing, OAuth 2.0 is still a draft spec, but is
nearing completion, and Chrome Extensions need some&amp;nbsp;love.&lt;/p&gt;

&lt;p&gt;You may be wondering why you even need an OAuth 2.0 library in the first place.
As Aaron Parecki pointed out in his &lt;a href="http://www.slideshare.net/aaronpk/the-current-state-of-oauth-2"&gt;Current State of OAuth 2&lt;/a&gt;
presentation at &lt;a href="http://opensourcebridge.org/"&gt;Open Source Bridge&lt;/a&gt; in Portland, OAuth 2 is very
much a moving target. The spec is not yet finalized, and there are 16 versions
of it (although the most popular seems to be v10). Also, today's OAuth 2
implementations diverge from the spec in varying degrees, adding to the
developer pain. The reason this library needs to be chrome extension-specific
is that unfortunately Chrome extensions can't directly use the OAuth 2.0
server-side or client-side flows because they live at &lt;code&gt;chrome-extension://&lt;/code&gt;
URLs.&lt;/p&gt;

&lt;p&gt;When writing the OAuth 2.0 library for Chrome extensions, I had some goals in&amp;nbsp;mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Support a variety of OAuth 2.0 providers that implement the&amp;nbsp;spec&lt;/li&gt;
&lt;li&gt;Allow one app/extension to use multiple different OAuth 2.0&amp;nbsp;endpoints&lt;/li&gt;
&lt;li&gt;Avoid background pages for performance&amp;nbsp;reasons&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;The OAuth 2.0&amp;nbsp;Library&lt;/h2&gt;

&lt;p&gt;There's a bit of setup involved if you'd like to create a Chrome extension that
connects to an OAuth 2 endpoint. This brief tutorial will guide you through
connecting to Google's&amp;nbsp;APIs.&lt;/p&gt;

&lt;p&gt;Register your application with an OAuth 2.0 endpoint that you'd like to
use. If it's a Google API you're calling, go to the &lt;a href="https://code.google.com/apis/console/"&gt;Google APIs&lt;/a&gt; page,
create your application and note your client ID and client secret. For more
info on this, check out the &lt;a href="http://code.google.com/apis/accounts/docs/OAuth2.html"&gt;Google OAuth 2.0&lt;/a&gt; docs. When you setup your
application, you will be asked to provide redirect URI(s). Please provide the
URI that corresponds to the service you're&amp;nbsp;using.&lt;/p&gt;

&lt;p&gt;Here's a table that will come in&amp;nbsp;handy:&lt;/p&gt;

&lt;p&gt;&lt;style&gt;
  #impls { margin-left: -100px; }
  #impls td, #impls th { border: 1px solid #999 }
  #impls td { padding: 5px }
&lt;/style&gt;&lt;/p&gt;

&lt;table id="impls"&gt;
  &lt;tr&gt;
    &lt;th&gt;Adapter&lt;/th&gt;
    &lt;th&gt;Redirect URI&lt;/th&gt;
    &lt;th&gt;Access Token URI&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;google&lt;/td&gt;
    &lt;td&gt;http://www.google.com/robots.txt&lt;/td&gt;
    &lt;td&gt;https://accounts.google.com/o/oauth2/token&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;facebook&lt;/td&gt;
    &lt;td&gt;http://www.facebook.com/robots.txt&lt;/td&gt;
    &lt;td&gt;https://graph.facebook.com/oauth/access_token&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;github&lt;/td&gt;
    &lt;td&gt;https://github.com/robots.txt&lt;/td&gt;
    &lt;td&gt;https://github.com/login/oauth/access_token&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;h4&gt;Step 1: Copy&amp;nbsp;library&lt;/h4&gt;

&lt;p&gt;You will need to copy the &lt;a href="https://github.com/borismus/oauth2-extensions/tree/master/lib"&gt;oauth2 library&lt;/a&gt; into your chrome extension
root into a directory called &lt;code&gt;oauth2&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;Step 2: Inject content&amp;nbsp;script&lt;/h4&gt;

&lt;p&gt;Then you need to modify your manifest.json file to include a content script
at the redirect URL used by the Google adapter. The "matches" redirect URI can
be looked up in the table&amp;nbsp;above:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;"content_scripts": [
  {
    "matches": ["http://www.google.com/robots.txt*"],
    "js": ["oauth2/oauth2_inject.js"],
    "run_at": "document_start"
  }
],
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Step 3: Allow access token&amp;nbsp;URL&lt;/h4&gt;

&lt;p&gt;Also, you will need to add a permission to Google's access token granting URL,
since the library will do an XHR against it. The access token URI can be looked
up in the table above as&amp;nbsp;well.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;"permissions": [
  "https://accounts.google.com/o/oauth2/token"
]
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Step 4: Include the OAuth 2.0&amp;nbsp;library&lt;/h4&gt;

&lt;p&gt;Next, in your extension's code, you should include the OAuth 2.0&amp;nbsp;library:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;script src="/oauth2/oauth2.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Step 5: Configure the OAuth 2.0&amp;nbsp;endpoint&lt;/h4&gt;

&lt;p&gt;And configure your OAuth 2 connection by providing clientId, clientSecret and
apiScopes from the registration page. The authorize() method may create a new
popup window for the user to grant your extension access to the OAuth2&amp;nbsp;endpoint.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var googleAuth = new OAuth2('google', {
  client_id: '17755888930840',
  client_secret: 'b4a5741bd3d6de6ac591c7b0e279c9f',
  api_scope: 'https://www.googleapis.com/auth/tasks'
});

googleAuth.authorize(function() {
  // Ready for action
});
&lt;/code&gt;&lt;/pre&gt;

&lt;h4&gt;Step 6: Use the access&amp;nbsp;token&lt;/h4&gt;

&lt;p&gt;Now that your user has an access token via &lt;code&gt;auth.getAccessToken()&lt;/code&gt;, you can
request protected data by adding the accessToken as a request&amp;nbsp;header&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;xhr.setRequestHeader('Authorization', 'OAuth ' + myAuth.getAccessToken())
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;or by passing it as part of the URL (depending on the server&amp;nbsp;implementation):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;myUrl + '?oauth_token=' + myAuth.getAccessToken();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: if you have multiple OAuth 2.0 endpoints that you would like to
authorize with, you can do that too! Just inject content scripts and add
permissions for all of the providers you would like to authorize&amp;nbsp;with.&lt;/p&gt;

&lt;p&gt;I've provided &lt;a href="https://github.com/borismus/oauth2-extensions/tree/master/samples"&gt;some sample extensions&lt;/a&gt; that use this library to help
you get&amp;nbsp;started.&lt;/p&gt;

&lt;h2&gt;Varying OAuth&amp;nbsp;implementations&lt;/h2&gt;

&lt;p&gt;Writing this library for one OAuth 2.0 endpoint was pretty straightforward.
The issues came when branching out to support multiple OAuth 2.0 server
implementations which comply to various degrees with differing versions of the&amp;nbsp;spec.&lt;/p&gt;

&lt;p&gt;Facebook was the worst offender here. They claim to be an OAuth 2.0
implementation in line with v10, but are actually quite far from it. Here are
some of the&amp;nbsp;issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Token request method is GET instead of&amp;nbsp;POST.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Token response is some strange form encoded format instead of&amp;nbsp;JSON.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://developers.facebook.com/docs/authentication/permissions/"&gt;List of scopes&lt;/a&gt; (aka "extended permissions") was really hard to&amp;nbsp;find.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apparently to get a user's favorite music, you need the &lt;code&gt;user_likes&lt;/code&gt;
permission. Facebook, please &lt;a href="http://forum.developers.facebook.net/viewtopic.php?pid=283691"&gt;fix your docs&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No refresh tokens but they have an offline_access permission which makes your
access token expire later. This is&amp;nbsp;ridiculous!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Twitter doesn't even have an OAuth 2.0 API. &lt;a href="http://dev.twitter.com/anywhere"&gt;@Anywhere&lt;/a&gt; does not
count. Some good &lt;a href="http://www.quora.com/Why-isnt-Twitter-implementing-OAuth-2-0-just-like-Facebooks"&gt;questions&lt;/a&gt; on &lt;a href="http://www.quora.com/When-is-Twitter-going-to-implement-OAuth-2-0"&gt;quora&lt;/a&gt; about&amp;nbsp;this.&lt;/p&gt;

&lt;p&gt;Still there are a lot of services that &lt;em&gt;DO&lt;/em&gt; implement OAuth 2.0, such as
Foursquare, Gowalla, Windows Live, Salesforce, Soundcloud and many&amp;nbsp;others.&lt;/p&gt;

&lt;h2&gt;Extending the&amp;nbsp;Library&lt;/h2&gt;

&lt;p&gt;To mitigate differences between OAuth 2.0 implementations, I implemented the
&lt;a href="http://en.wikipedia.org/wiki/Adapter_pattern"&gt;Adapter pattern&lt;/a&gt;. Doing this encapsulates protocol differences
in a separate adapter module for each server&amp;nbsp;implementation.&lt;/p&gt;

&lt;p&gt;The library comes with adapters for Google, Facebook and Github. These adapters
are located in the &lt;a href="https://github.com/borismus/oauth2-extensions/tree/master/lib/adapters"&gt;adapters directory&lt;/a&gt; here. If you would like to
contribute your own adapter, please take a look at the sample adapter and then
&lt;a href="https://github.com/borismus/oauth2-extensions"&gt;fork the project&lt;/a&gt;, submit a pull request, and I'll try to add
it to the&amp;nbsp;project.&lt;/p&gt;

&lt;p&gt;Also, please let me know if you experience problems using this library and
we'll sort them out! The best way to do this is via &lt;a href="http://github.com/borismus"&gt;github&lt;/a&gt; or
&lt;a href="http://twitter.com/borismus"&gt;twitter&lt;/a&gt;.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/oauth2-chrome-extensions</feedburner:origLink></entry><entry><title>Brazil Trip</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/gOmGKfQr89Y/brazil-trip" /><updated>2011-06-21T09:00:00Z</updated><published>2011-06-21T09:00:00Z</published><id>http://smus.com/brazil-trip</id><content type="html">
      
&lt;h1&gt;
  Brazil&amp;nbsp;Trip
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="June 21, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;June 21, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Last month I took a work trip to São Paulo, Brazil. I gave four Chrome/HTML5
presentations and talked to many engineers and designers over the course of the
week, trying hard not to sound like a broken record. Luckily Brazil has a lot
of people so the audiences were different each time! Here's a link to my
&lt;a href="http://smustalks.appspot.com/brazil-11/"&gt;slides&lt;/a&gt;, that I tweaked slightly depending on the&amp;nbsp;audience.&lt;/p&gt;

&lt;p&gt;I'll be dumping all of my slide decks and/or talk videos to &lt;a href="http://smustalks.appspot.com/"&gt;smustalks&lt;/a&gt; on
AppEngine. I've made the switch from building slides in Keynote to using this
fantastic HTML5 &lt;a href="http://code.google.com/p/html5slides/"&gt;slide deck&lt;/a&gt; template and my favorite &lt;a href="http://www.vim.org/"&gt;text editor&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Food&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Delicious fruits of all varieties: exotic cashew, persimmon, star fruit, guyava, and way tastier&amp;nbsp;bananas.&lt;/li&gt;
&lt;li&gt;Ridiculously massive portions of sashimi at japanese&amp;nbsp;restaurants.&lt;/li&gt;
&lt;li&gt;The Rodizio we went to was super exquisite. Largest salad bar ever, including caviar&amp;nbsp;(srsly).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Life&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SP is a concrete jungle like nothing I've ever seen. The whole city is built up within a 60km&amp;nbsp;radius.&lt;/li&gt;
&lt;li&gt;Chaotic traffic patterns and insane motorcyclists abound. Everyone loves to drive between lanes and honk&amp;nbsp;gratuitously.&lt;/li&gt;
&lt;li&gt;Super bumpy roads date back to the dictatorship days. If you take the wrong exit, prepare to jump&amp;nbsp;around.&lt;/li&gt;
&lt;li&gt;Helicopters fly all over town and sometimes land precariously close to unsuspecting window cleaners dangling from highrise&amp;nbsp;roofs.&lt;/li&gt;
&lt;li&gt;Beware foul smelling rivers and slums on the way to Friday morning&amp;nbsp;meetings!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Work&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Super friendly people but my Portuguese skills failed to topple the language&amp;nbsp;barrier.&lt;/li&gt;
&lt;li&gt;Long process to get into any office building. Some ask for ID, others ask for passport. Takes half an hour just to get&amp;nbsp;in...&lt;/li&gt;
&lt;li&gt;Somehow, Google Brazil still feels like&amp;nbsp;Google!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After my work was done, I took some &lt;a href="https://picasaweb.google.com/boris.smus/Brazil"&gt;travel photos&lt;/a&gt; and heard
&lt;a href="https://picasaweb.google.com/boris.smus/Brazil#5612942830070183426"&gt;Samba da Minha Terra&lt;/a&gt; live, and by so doing, joined the ranks of Don and&amp;nbsp;Magdalena!&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;I've never sailed the Amazon,
I've never reached Brazil;
But the Don and Magdalena,
They can go there when they will!

            - R. Kipling
&lt;/code&gt;&lt;/pre&gt;



    </content><feedburner:origLink>http://smus.com/brazil-trip</feedburner:origLink></entry><entry><title>Dynamic Chrome Extension Icons</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/h5u1RHMfvWs/dynamic-icons-chrome-extensions" /><updated>2011-06-06T09:00:36Z</updated><published>2011-06-06T09:00:36Z</published><id>http://smus.com/dynamic-icons-chrome-extensions</id><content type="html">
      
&lt;h1&gt;
  Dynamic Chrome Extension&amp;nbsp;Icons
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="June 6, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;June 6, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Extension developers aren't given much freedom to modify Chrome's browser
chrome. Without resorting to changing the page itself, or using the new devtools
extension APIs, there are two main ways of doing this. &lt;a href="http://code.google.com/chrome/extensions/pageAction.html"&gt;Page actions&lt;/a&gt;, which
reside in the omnibox, and &lt;a href="http://code.google.com/chrome/extensions/browserAction.html"&gt;browser actions&lt;/a&gt;, which are positioned to the
right of the omnibox both of which are simple buttons with icons, click actions
and hover states. Chrome conveniently &lt;a href="http://code.google.com/chrome/extensions/browserAction.html#method-setIcon"&gt;provides an API&lt;/a&gt; to dynamically change
the icon of these&amp;nbsp;buttons.&lt;/p&gt;

&lt;p&gt;You can do this by creating image data by hand or using the canvas API's
&lt;code&gt;getImageData&lt;/code&gt; function.:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
// ...draw to the canvas...
var imageData = context.getImageData(0, 0, 19, 19);
chrome.browserAction.setIcon({
  imageData: imageData
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that this would be possible even without this chrome-specific API, by
instead using &lt;a href="http://en.wikipedia.org/wiki/Data_URI_scheme"&gt;data URIs&lt;/a&gt; to set the image. There's a GMail &lt;a href="http://googlesystem.blogspot.com/2011/01/dynamic-gmail-favicon.html"&gt;labs plugin&lt;/a&gt;
that does this to badge the favicon with the unread email&amp;nbsp;count.&lt;/p&gt;

&lt;p&gt;What makes this an interesting design domain is the limitation of the medium.
Limited real estate (browser actions at 19x19 px, page actions at 16x16 px) adds
significant constraints. Still, one can show numbers, colors, small icons and
graphs in this context, or even small amounts of&amp;nbsp;text.&lt;/p&gt;

&lt;h2&gt;Applications of Dynamic&amp;nbsp;Icons&lt;/h2&gt;

&lt;p&gt;There are extensions that use dynamic icons already, such as the
&lt;a href="https://chrome.google.com/webstore/detail/kpekpmmfocifmbnnoahnclccmjkckpcl"&gt;PageRank extension&lt;/a&gt;, which effectively shows the Google PageRank for the
current page right inside the browser&amp;nbsp;action.&lt;/p&gt;

&lt;p&gt;Here are some other possibilities (not at all an exhaustive&amp;nbsp;list):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create badges for page actions (which don't implement &lt;code&gt;setBadge*&lt;/code&gt; calls).&lt;/li&gt;
&lt;li&gt;Icon of the weather forecast, click to toggle between&amp;nbsp;days.&lt;/li&gt;
&lt;li&gt;Bandwidth meter: how large was the download of this&amp;nbsp;page?&lt;/li&gt;
&lt;li&gt;A random profile pic of people that +1'ed the&amp;nbsp;site.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;The Smallest Music&amp;nbsp;Visualizer&lt;/h2&gt;

&lt;p&gt;I wrote a sample extension to demonstrate the dynamic icon potential of Chrome
extensions. This indispensible extension is a music visualizer that renders
inside of a browser action button. I use the &lt;a href="http://chromium.googlecode.com/svn/trunk/samples/audio/specification/specification.html"&gt;Web Audio API&lt;/a&gt; to playback a
song and analyse the audio stream, render the visualized audio spectrum with a
canvas element and then transfer the resulting image data to the browser action&amp;nbsp;icon.&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2011/music-vis.png" alt="screenshot" /&gt;&lt;/p&gt;

&lt;p&gt;Try out the &lt;a href="https://chrome.google.com/webstore/detail/befnabfghcghgpmkjoalbecphdgdmick?hl=en"&gt;chrome extension&lt;/a&gt; here, but note that it requires the Web Audio
API flag to be enabled under about:flags (and a browser restart afterward).
Check out and fork the &lt;a href="https://github.com/borismus/Music-Visualizer-Chrome-Extension"&gt;source on github&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Learnings&lt;/h2&gt;

&lt;p&gt;This music visualizer extension loads an mp3 file when the extension
background page loads, which takes a certain amount of time. To provide a
better user experience, I was hoping to change the icon to reflect that the
file was being loaded, and ran into two&amp;nbsp;issues.&lt;/p&gt;

&lt;p&gt;The first issue was that when I tried to render a small string like "wait" in
the icon, I wanted to use a custom &lt;code&gt;@font-face&lt;/code&gt; embedded font, which is now
well supported in CSS3. You can load CSS&amp;nbsp;fonts&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@font-face {
  font-family: "Silkscreen"
  src: url(slkscr.ttf);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and then use them in a&amp;nbsp;canvas:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;context.font = "8px Silkscreen";
context.fillText('load');
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When using custom fonts from HTML, the browser waits for the font to load, and
then does a relayout. When using it from canvas, things get a bit tricky since
the browser of course doesn't do font relayout for you and there's
unfortunately no DOM event that fires when all embedded fonts finished loading.
More precisely, onload behaviors differ from browser to browser. Mozilla waits
for all fonts to load before firing the event, WebKit doesn't. You can work
around this problem by assigning the custom font to a div, and observing the
div's width, which will change when the font loads (&lt;a href="https://github.com/paulirish/font-face-detect/blob/master/isFontLoaded.js"&gt;codified&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Although I ultimately didn't use this font to show loading state, I recommend
checking out the &lt;a href="http://kottke.org/plus/type/silkscreen/"&gt;silkscreen font&lt;/a&gt; for 8-bit style designs that lend
themselves well to small resolution envirnoments. You can fit about 3x3
characters inside a 16x16&amp;nbsp;canvas:&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2011/silkscreen.png" alt="silkscreen" /&gt;&lt;/p&gt;

&lt;p&gt;To show that the extension is still loading, I went with a progress bar instead
of a message. A second issue arose when I wanted to show the progress
bar animating while the mp3 loads. Unfortunately the Web Audio API doesn't
currently support asynchronous loading of files, so the UI thread gets blocked
during the &lt;code&gt;audioContext.createBuffer&lt;/code&gt; call of this code&amp;nbsp;snippet:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var request = new XMLHttpRequest();
request.onload = function() {
  var audioBuffer = audioContext.createBuffer(request.response, false);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Async loading of audio buffers is now a &lt;a href="https://bugs.webkit.org/show_bug.cgi?id=61947"&gt;tracked issue&lt;/a&gt; for you upvote in
the webkit bug tracker. I thought of working around this with Web Workers, but
gave up early because of difficulties with passing objects between worker
threads, and no shared memory options that would let workers access the
context of the main UI&amp;nbsp;thread.&lt;/p&gt;

&lt;p&gt;Another interesting observation is that &lt;code&gt;requestAnimationFrame&lt;/code&gt; does not work in
a background page. I initially tried to use it to animate the music visualizer,
but it didn't work. This is of course the API is designed to only callback when
the calling page is in the foreground, and since the background page is never
foregrounded, the callback never&amp;nbsp;fires.&lt;/p&gt;

&lt;p&gt;That's it for me, now it's your turn! So, dearest reader, go forth and write
some awesome Chrome extensions which tastefully use dynamic icons for page
actions, browser actions, and favicons to make our browsing experience even&amp;nbsp;better.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/dynamic-icons-chrome-extensions</feedburner:origLink></entry><entry><title>Crunching Numbers for the NHL Finals</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/ERz9kUQBZy0/nhl-finals-numbers" /><updated>2011-05-31T09:00:36Z</updated><published>2011-05-31T09:00:36Z</published><id>http://smus.com/nhl-finals-numbers</id><content type="html">
      
&lt;h1&gt;
  Crunching Numbers for the NHL&amp;nbsp;Finals
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="May 31, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;May 31, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Now that the Vancouver Canucks are in the NHL Finals, a special visit to
Vancouver is in order. Unfortunately, the recently posted schedule really
sucks. There’s just one weekend game, and the rest are spaced in such a way
that I can only hope to see Game 5 (if it happens) and either Game 4 or Game 6
(if it happens) if I manage to work from home half of the week. The question
is: which game to choose? I want to be in Vancouver when the Canucks take the&amp;nbsp;cup!&lt;/p&gt;

&lt;h2&gt;Yay&amp;nbsp;math!&lt;/h2&gt;

&lt;p&gt;Bring super nerdy, I decided to throw math at the problem. Brushing up on
probability, I calculated the odds of a series ending at each game (4-7). For a
series to end in game 4, one team has to win all four games. The odds of this,
given a 50% chance for each team to win, is (1/2)^4. Since there are two teams,
multiply by two, resulting in a 1/8 chance. You can do similar calcuations to
get the following&amp;nbsp;odds:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;p(4)   p(5)   p(6)   p(7)
0.125  0.25   0.3125 0.3125
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Based on this distribution, it seems that game 6 is more likely to see the end
of the series than game 4, but then I would risk missing a game 4 victory.
&lt;a href="http://www.ted.com/talks/arthur_benjamin_s_formula_for_changing_math_education.html"&gt;Arthur Benjamin&lt;/a&gt; would be proud of me (unless my numbers are&amp;nbsp;wrong).&lt;/p&gt;

&lt;h2&gt;A bit of&amp;nbsp;history&lt;/h2&gt;

&lt;p&gt;Historical data tells a different story. As it turns out, the NHL is really
old, dating back to 1927, but the league switched to best-of-7 scoring in 1939.
Wikipedia has a great chronology of NHL playoffs, complete with scores and&amp;nbsp;brackets.&lt;/p&gt;

&lt;p&gt;I analyzed series scores from all playoff games in the last 25 years (since
1985) by fetching raw wikipedia articles via wget and running them through a
[python script]. Older records can also be found on wikipedia, but they are
buried inside other pages, in harder to parse&amp;nbsp;formats.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;for year in {1985..2011} do
  wget -O ${year} "http://en.wikipedia.org/w/index.php?title=${year}_Stanley_Cup_playoffs&amp;amp;action=raw"
done
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This yielded the following distribution for playoff games, based on 383&amp;nbsp;matches:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;p(4)   p(5)   p(6)   p(7)
0.16   0.24   0.33   0.27
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For finals games only, the distribution is more skewed, probably in part due to
a shortage of data (just NN finals games from the wikipedia page), but finals
game history is probably a better predictor for finals games than all playoffs
games, so this is&amp;nbsp;worthwhile:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;p(4)   p(5)   p(6)   p(7)
0.28   0.24   0.27   0.21
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here are all three distributions plotted in a&amp;nbsp;graph:&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2011/nhl-series-odds.png" alt="graph" /&gt;&lt;/p&gt;

&lt;h2&gt;Analyzing...&lt;/h2&gt;

&lt;p&gt;The most striking thing about this graph is the huge difference between finals
history and stats, especially in the 4 game series scenario. Perhaps some teams
just buckle under the pressure, while their opponents remain steadfast. Or this
is just a statistically insignificant fluke that can be attributed to having an
insufficiently large sample of finals&amp;nbsp;matches.&lt;/p&gt;

&lt;p&gt;As expected, the historical distributions are skewed toward the series
finishing in Game 4 due to unequal strength between teams. In other words, if
one team is stronger than another, then the odds of that team winning each game
would be greater, making the series shorter on&amp;nbsp;average.&lt;/p&gt;

&lt;h2&gt;Decision!&lt;/h2&gt;

&lt;p&gt;Armed with numbers, I can decide whether to pick Game 4 or Game 6. Here are the
odds of seeing the Canucks (yeah yeah, maybe the Bruins) win in games 4 or 5,
compared to 5 or 6, as calculated from each of the three&amp;nbsp;methods:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;         Game 4 or 5            Game 5 or 6
Stats        38%                    58%
Playoffs     40%                    57%
Finals       52%                    51%
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Interesting. Given how annoying it would be to miss a Game 4 victory and how
favorable the finals history-based odds look, I’ll go for 4 and 5. Now to try
to work from home, book flights and bask in Canuck&amp;nbsp;glory!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/nhl-finals-numbers</feedburner:origLink></entry><entry><title>Chrome Developer Tools Cheatsheet</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/KQNjFgFUrmQ/devtools-cheatsheet" /><updated>2011-05-13T09:00:00Z</updated><published>2011-05-13T09:00:00Z</published><id>http://smus.com/devtools-cheatsheet</id><content type="html">
      
&lt;h1&gt;
  Chrome Developer Tools&amp;nbsp;Cheatsheet
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="May 13, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;May 13, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Chrome's Developer Tools have been getting much deserved love at this last
Google I/O. Paul Irish and I started with an &lt;a href="http://www.io-bootcamp.com/"&gt;bootcamp lab&lt;/a&gt;, a hands-on 
walk through tweaking a web application using the developer tools. We handed
out a cheatsheet to give developers an overview of available&amp;nbsp;features.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/borismus/DevTools-Lab/raw/master/cheatsheet/chromedev-cheatsheet.pdf"&gt;&lt;img src="https://github.com/borismus/DevTools-Lab/raw/master/cheatsheet/chromedev-cheatsheet.jpg" alt="cheatsheet jpg" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The cheatsheet is available for download in &lt;a href="https://github.com/borismus/DevTools-Lab/raw/master/cheatsheet/chromedev-cheatsheet.pdf"&gt;PDF&lt;/a&gt; and
&lt;a href="https://github.com/borismus/DevTools-Lab/raw/master/cheatsheet/chromedev-cheatsheet.png"&gt;PNG&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Thanks to the awesome I/O organizers, we also gave away free HTML5 cake (as
promised) after the session&amp;nbsp;ended:&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2011/html5-cake.jpg" alt="html5 cake" /&gt;&lt;/p&gt;

&lt;p&gt;The developer tools were also featured at the &lt;a href="http://www.youtube.com/watch?v=MiYND_zvIc0&amp;amp;t=8m30s"&gt;start of the Chrome keynote&lt;/a&gt;
with a webkit-speech demo. Finally, Pavel Feldman (lead engineer for the dev
tools) and Paul gave an great &lt;a href="http://www.youtube.com/watch?v=N8SS-rUEZPg"&gt;I/O talk&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One of the most promising aspects of the Chrome Developer Tools is that they
are easy to extend with (experimental) &lt;a href="http://code.google.com/chrome/extensions/trunk/experimental.devtools.html"&gt;chrome extension APIs&lt;/a&gt;. I'm stoked to
see what people create to make these developer tools even&amp;nbsp;better!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/devtools-cheatsheet</feedburner:origLink></entry><entry><title>Multi-Touch for your Desktop Browser</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/jsLSmcdgY3g/multi-touch-browser-patch" /><updated>2011-05-02T09:00:36Z</updated><published>2011-05-02T09:00:36Z</published><id>http://smus.com/multi-touch-browser-patch</id><content type="html">
      
&lt;h1&gt;
  Multi-Touch for your Desktop&amp;nbsp;Browser
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="May 2, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;May 2, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;In mobile development, it's often easier to start prototyping on the desktop
and then tackle the mobile-specific parts on the devices you intend to support.
Multi-touch is one of those features that's difficult to test on the desktop, since
most desktops didn't have multi-touch hardware, and thus desktop browsers don't
have touch event support. Things are different today (you hear every mother say). 
Most new Macs, for example, ship with multi-touch capable input of some sort.
Unfortunately the browsers haven't really caught up&amp;nbsp;yet.&lt;/p&gt;

&lt;p&gt;Enter Fajran Iman Rusadi, who released a &lt;a href="https://github.com/fajran/npTuioClient"&gt;npTuioClient&lt;/a&gt; NPAPI plugin with a
JavaScript wrapper. Unfortunately this library provides a non-standard API to
multi-touch, which is not ideal for developers that want to write their
multi-touch application on desktop and then run the same code on their mobile
devices without&amp;nbsp;modifications.&lt;/p&gt;

&lt;h2&gt;Browser&amp;nbsp;Patches&lt;/h2&gt;

&lt;p&gt;As HTML5 grows up, browser vendors struggle to stay current up with the growing
variety of specifications. The result is &lt;a href="http://caniuse.com/"&gt;uneven feature support&lt;/a&gt; across
browsers and a complex problem for web&amp;nbsp;developers.&lt;/p&gt;

&lt;p&gt;The web development community has rallied around &lt;strong&gt;shims&lt;/strong&gt; and &lt;strong&gt;polyfills&lt;/strong&gt;
for the solution. These are bizarre terms that I find confusing and so will
defer to &lt;a href="http://remysharp.com/2010/10/08/what-is-a-polyfill/"&gt;Remy Sharp to define&lt;/a&gt;. The basic idea of both is to fill in
functionality that's missing in the browser&amp;nbsp;implementation.&lt;/p&gt;

&lt;p&gt;Since we now have a well established &lt;a href="https://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html"&gt;touch events specification&lt;/a&gt; working
group at the W3C, I wrote &lt;a href="https://github.com/borismus/MagicTouch"&gt;MagicTouch.js&lt;/a&gt;, a multi-touch polyfill thatlets
you, the developer, write the same code, test it on your desktop browser and
then, run it on your real device. Totally&amp;nbsp;tubular!&lt;/p&gt;

&lt;p&gt;MagicTouch.js still relies on the npTuioClient plugin, it just creates
spec-compatible touch events. Incidentally, here's how you can trigger custom
DOM&amp;nbsp;events:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var event = document.createEvent('CustomEvent');
// Initialize the event, make it bubble up and possible to cancel
event.initEvent('touchstart', true, true);
// Assign properties to the event
event.touches = touchArray;
...
// Get the element associated with the event
var element = document.elementFromPoint(...);
// Assign the element
event.target = element;
// Finally, dispatch the event
element.dispatchEvent(event);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that this approach to create custom DOM events is not cross-browser
compatible. I only tested in&amp;nbsp;Chrome.&lt;/p&gt;

&lt;h2&gt;Installation&lt;/h2&gt;

&lt;p&gt;Here how to get multi-touch web events working in Chrome for&amp;nbsp;Mac:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download and install the &lt;a href="https://github.com/fajran/npTuioClient#readme"&gt;npTuioClient NPAPI plugin&lt;/a&gt;
into &lt;code&gt;~/Library/Internet Plug-Ins/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Download the &lt;a href="https://github.com/fajran/tongseng/downloads"&gt;TongSeng TUIO tracker&lt;/a&gt; for Mac’s MagicPad and start the&amp;nbsp;server&lt;/li&gt;
&lt;li&gt;Download &lt;a href="https://github.com/borismus/MagicTouch"&gt;MagicTouch.js&lt;/a&gt; and include both the script and the plugin in your&amp;nbsp;app.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The code for this is as&amp;nbsp;follows:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;head&amp;gt;
  ...
  &amp;lt;script src="/path/to/magictouch.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  ...
  &amp;lt;object id="tuio" type="application/x-tuio" style="width:0; height:0;"&amp;gt;
    TUIO Plugin failed to load
  &amp;lt;/object&amp;gt;
&amp;lt;/body&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;...and you're off to the races! Your multi-touch code will now work. Try out
this &lt;a href="https://github.com/borismus/MagicTouch/blob/master/samples/tracker.html"&gt;finger tracking demo&lt;/a&gt; on either your multi-touch mobile device or your
newly patched desktop&amp;nbsp;browser.&lt;/p&gt;

&lt;h2&gt;Future&amp;nbsp;Steps&lt;/h2&gt;

&lt;p&gt;As you saw, MagicTouch.js takes some effort to set up initially, requires
you to use an &lt;code&gt;&amp;lt;object&amp;gt;&lt;/code&gt; in the HTML, and also needs you to run a separate
process for intercepting touch events. While we can't quite get away without
having to run another process, we can eliminate the NPAPI plugin by using the
&lt;a href="http://dev.w3.org/html5/websockets/"&gt;WebSocket API&lt;/a&gt; to communicate to that&amp;nbsp;process.&lt;/p&gt;

&lt;p&gt;If you're interested in multi-touch mobile web development, check out this
&lt;a href="http://www.html5rocks.com/mobile/touch.html"&gt;article on html5rocks.com&lt;/a&gt;.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/multi-touch-browser-patch</feedburner:origLink></entry><entry><title>jQuery Conference 2011</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/FxTXYUMF7ro/jquery-conference" /><updated>2011-04-26T09:00:36Z</updated><published>2011-04-26T09:00:36Z</published><id>http://smus.com/jquery-conference</id><content type="html">
      
&lt;h1&gt;
  jQuery Conference&amp;nbsp;2011
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="April 26, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;April 26, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;A few weekends ago I went to the jQuery Conference held at the MS campus in
Mountain View. And I took&amp;nbsp;notes!&lt;/p&gt;

&lt;p&gt;Overall trends about the jQuery&amp;nbsp;community:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;People are writing more complex apps on top of jQuery and there
is a widely understood need for MVC frameworks, such as &lt;a href="http://documentcloud.github.com/backbone/"&gt;Backbone.js&lt;/a&gt;, 
&lt;a href="http://knockoutjs.com/"&gt;Knockout.js&lt;/a&gt; and &lt;a href="http://javascriptmvc.com/"&gt;JavaScript MVC&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Feature detection is&amp;nbsp;important!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Polyfill - replicates standard feature with a compatible&amp;nbsp;API&lt;/li&gt;
&lt;li&gt;Shim - provides its own API for a future&amp;nbsp;feature&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Serious need for templating systems. Boris Moore showed a very performant
demo of jQuery Templates. Many other templating systems exist as well,
like one built into &lt;a href="http://documentcloud.github.com/underscore/"&gt;underscore.js&lt;/a&gt; and &lt;a href="http://mustache.github.com/"&gt;mustache.js&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Many new mobile performance tools: &lt;a href="http://www.blaze.io/"&gt;blaze.io&lt;/a&gt; -- a tool that gives a
general overview of a site's performance, &lt;a href="http://pcapperf.appspot.com/"&gt;pcapperf&lt;/a&gt; -- a web performance
analyzer that uses tcpdump output from mobile device activity, and &lt;a href="http://jdrop.org/"&gt;jDrop&lt;/a&gt;
-- a service that lets you capture large amounts of data on your mobile
device and then analyze it on the desktop web&amp;nbsp;browser.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;People are rallying around &lt;a href="http://jshint.com/"&gt;JSHint&lt;/a&gt;, a fork of Crockford's &lt;a href="http://www.jslint.com/"&gt;JSLint&lt;/a&gt;
project, but with more configurable JavaScript sanitation&amp;nbsp;rules.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Haters gotta hate. Everybody seems to get a kick out of hating Douglas
Crockford. Give the nice opinionated man a break and go write some&amp;nbsp;JavaScript.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I went to a bunch of talks, and I took the most notes for during this&amp;nbsp;talk:&lt;/p&gt;

&lt;h2&gt;State of&amp;nbsp;jQuery&lt;/h2&gt;

&lt;p&gt;John Resig talked about a bunch of changes to the project structure, largely
irrelevant to jQuery library consumers. He also covered some of many jQuery 1.6&amp;nbsp;improvements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rewrite of &lt;code&gt;attr()&lt;/code&gt; and &lt;code&gt;val()&lt;/code&gt;. For example, &lt;code&gt;attr('val', false)&lt;/code&gt; removes
the&amp;nbsp;attribute&lt;/li&gt;
&lt;li&gt;Separate &lt;code&gt;prop()&lt;/code&gt; from &lt;code&gt;attr()&lt;/code&gt;.&amp;nbsp;Indeed!&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$('input:focus')&lt;/code&gt; gets focused input box across&amp;nbsp;platforms&lt;/li&gt;
&lt;li&gt;Significant performance boosts:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;attr()&lt;/code&gt; performance ~85% faster, &lt;code&gt;val()&lt;/code&gt; ~150% faster, &lt;code&gt;data()&lt;/code&gt; ~115%&amp;nbsp;faster&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Integration with requestAnimationFrame for&amp;nbsp;animations&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$.map(Object, function)&lt;/code&gt; now works (as it does for&amp;nbsp;Arrays)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pro tip: jQuery automatically parses serialized JSON if it's included as the value 
of a HTML5 data attribute. Example: &lt;code&gt;&amp;lt;header data-array="[0,1,2]"&amp;gt;&lt;/code&gt; then 
&lt;code&gt;$('header').data('array')[1] == 1&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;State of jQuery&amp;nbsp;Mobile&lt;/h2&gt;

&lt;p&gt;Mobile matters. 5.3 billion mobile subscriptions (cf. global population of 6.8
billion), 10 billion web-enabled mobile&amp;nbsp;devices.&lt;/p&gt;

&lt;p&gt;John Resig also touched on jQuery Mobile, and then Scott Jehl and Todd Parker went 
into a lot more&amp;nbsp;detail.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigation model now uses the &lt;a href="https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history"&gt;history API&lt;/a&gt; for hash-less&amp;nbsp;URLs.&lt;/li&gt;
&lt;li&gt;jQM minified and packed is&amp;nbsp;~18kb!&lt;/li&gt;
&lt;li&gt;Nice gallery of goodness at &lt;a href="http://www.jqmgallery.com/"&gt;jQuery Mobile Gallery&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Including &lt;a href="http://www.barackobama.com/m/"&gt;Obama's mobile site&lt;/a&gt;!&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Media queries
&lt;ul&gt;
&lt;li&gt;Useful as a browser support cutoff&amp;nbsp;heuristic.&lt;/li&gt;
&lt;li&gt;CSS classes added based on media queries, facilitating simpler&amp;nbsp;styles&lt;/li&gt;
&lt;li&gt;Uses &lt;a href="https://github.com/scottjehl/Respond"&gt;Respond.js&lt;/a&gt;, a polyfill for browsers that don't support media&amp;nbsp;queries&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Philosophy: easily brandable cross-device&amp;nbsp;experience&lt;/li&gt;
&lt;li&gt;All builtin views are&amp;nbsp;ARIA-enabled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pro tip: mouse events in some mobile browsers are on a &lt;a href="http://cubiq.org/remove-onclick-delay-on-webkit-for-iphone"&gt;300ms delay&lt;/a&gt; to allow
the browser to interpret user's gestures. jQuery Mobile includes a fix for&amp;nbsp;this!&lt;/p&gt;

&lt;h2&gt;Prototyping Tools in&amp;nbsp;jQuery&lt;/h2&gt;

&lt;p&gt;Super useful and informative set of&amp;nbsp;tools!&lt;/p&gt;

&lt;p&gt;MockJAX is a library that simulates a&amp;nbsp;server.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intercepts and simulates AJAX calls
&lt;ul&gt;
&lt;li&gt;Define a URL structure and a response&amp;nbsp;structure&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Can define responses as a&amp;nbsp;function.&lt;/li&gt;
&lt;li&gt;Can simulate error&amp;nbsp;responses.&lt;/li&gt;
&lt;li&gt;Useful for unit testing as&amp;nbsp;well!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MockJSON: create fake JSON on&amp;nbsp;demand&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A way to generate random-ish&amp;nbsp;JSON&lt;/li&gt;
&lt;li&gt;For example, &lt;code&gt;{'age|0-99'}&lt;/code&gt; outputs &lt;code&gt;{'age': randint_between_0_and_99}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Amplify: abstraction layer for all&amp;nbsp;data&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Abstracts away shifting server-side&amp;nbsp;APIs&lt;/li&gt;
&lt;li&gt;amplify.request.define can define a data&amp;nbsp;store. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For&amp;nbsp;example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;amplify.request.define("list", "ajax", {
  url: "/todo/",
  dataType: "json",
  type: "GET"
});
&lt;/code&gt;&lt;/pre&gt;



    </content><feedburner:origLink>http://smus.com/jquery-conference</feedburner:origLink></entry><entry><title>From Wordpress to Hyde</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/uyR9C6QRtmk/wordpress-to-hyde" /><updated>2011-04-20T09:00:36Z</updated><published>2011-04-20T09:00:36Z</published><id>http://smus.com/wordpress-to-hyde</id><content type="html">
      
&lt;h1&gt;
  From Wordpress to&amp;nbsp;Hyde
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="April 20, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;April 20, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;I've used &lt;a href="http://wordpress.com/"&gt;Wordpress&lt;/a&gt; for a couple of years now as my web publishing platform of
choice. I customized it a little bit, and made a &lt;a href="/new-design"&gt;custom theme&lt;/a&gt; for it. But 
nothing is perfect, and neither is wordpress. As a result of relatively heavy
use, I've collected a list of things I don't much like about&amp;nbsp;it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can't upload source code since it restricts allowed file&amp;nbsp;types.&lt;/li&gt;
&lt;li&gt;Can't easily edit files with a regular text&amp;nbsp;editor.&lt;/li&gt;
&lt;li&gt;Painful theme&amp;nbsp;customization.&lt;/li&gt;
&lt;li&gt;PHP makes it unpleasant to&amp;nbsp;extend.&lt;/li&gt;
&lt;li&gt;Constant security upgrades&amp;nbsp;required.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these things separately are perhaps small and insignificant, but
together add up to create a certain barrier to&amp;nbsp;blogging.&lt;/p&gt;

&lt;h2&gt;Static&amp;nbsp;files&lt;/h2&gt;

&lt;p&gt;A blog is mostly static. Why should my article be stored in a database? It's hardly 
ever queried or searched. Comments are perhaps the exception here, but services
like &lt;a href="http://disqus.com/"&gt;Disqus&lt;/a&gt; implement commenting with embeddable JavaScript. And in general,
dynamic parts can be handled in client-side&amp;nbsp;code.&lt;/p&gt;

&lt;p&gt;So I looked to static site generators. There's about &lt;a href="http://stackoverflow.com/questions/186290/best-static-website-generator"&gt;a million&lt;/a&gt; of these,
the most prominent of which is &lt;a href="https://github.com/mojombo/jekyll"&gt;Jekyll&lt;/a&gt;, written by the github co-founder. 
Ultimately I chose &lt;a href="https://github.com/lakshmivyas/hyde/"&gt;Hyde&lt;/a&gt; for it's use of &lt;a href="http://docs.djangoproject.com/en/dev/ref/templates/builtins/"&gt;Django templates&lt;/a&gt; and&amp;nbsp;Python.&lt;/p&gt;

&lt;h2&gt;Migrating the old&amp;nbsp;blog&lt;/h2&gt;

&lt;p&gt;The old blog was written in HTML, but I wanted to switch to a format that can
be written faster, such as Markdown, Textile, or many others. To do this I used
a tool called exitwp, which parses the Wordpress export and generates Markdown
files appropriate for Jekyll. I &lt;a href="https://github.com/borismus/exitwp"&gt;forked exitwp&lt;/a&gt; and hacked it to generate
files better suited to Hyde. Thanks &lt;a href="http://thomas.jossystem.se/"&gt;Thomas Frössman&lt;/a&gt; for&amp;nbsp;exitwp.&lt;/p&gt;

&lt;p&gt;I did spent a fair amount of time tweaking the export, replacing Smart YouTube URLs
with real YouTube embeds, and eliminating Syntax Highlighter&amp;nbsp;markup.&lt;/p&gt;

&lt;h2&gt;Customizing&amp;nbsp;hyde&lt;/h2&gt;

&lt;p&gt;Hyde is basically built on top of Django templates, so is customizable with
template tags and filters, written in Python. It comes with a bunch of them,
including ones that parse markdown and other structured text formats into&amp;nbsp;HTML.&lt;/p&gt;

&lt;p&gt;Surprisingly I didn't need to customize Hyde much, although I did fix a couple of 
bugs, which the author, &lt;a href="http://ringce.com/"&gt;Lakshmi Vyas&lt;/a&gt; accepted. Anyway, I feel much more
future proof now than with Wordpress, since I can easily write extensions in
Python, or in the worst case take my Markdown-formatted blog posts and easily
export them to many different&amp;nbsp;formats.&lt;/p&gt;

&lt;h2&gt;Dynamic&amp;nbsp;stuff&lt;/h2&gt;

&lt;p&gt;The bit of custom JS for the site layout is written using&amp;nbsp;jQuery.&lt;/p&gt;

&lt;p&gt;I use the excellent &lt;a href="http://softwaremaniacs.org/soft/highlight/en/"&gt;highlight.js&lt;/a&gt; plugin to highlight code snippets. The
jQuery &lt;a href="http://timeago.yarp.com/"&gt;timeago&lt;/a&gt; plugin does a great job of converting absolute dates (April
1, 2010) to relative dates (about a year ago). Finally, Disqus powers comments,
which I migrated from the wordpress&amp;nbsp;database.&lt;/p&gt;

&lt;h2&gt;New&amp;nbsp;design&lt;/h2&gt;

&lt;p&gt;In my great wisdom, I decided that since there's so much flux in the blog, why not
add some more chaos by switching to a new layout too? Here's the old&amp;nbsp;layout:&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2009/new_design.png" alt="old" /&gt;&lt;/p&gt;

&lt;p&gt;I recently heard from sage advice from some experienced writers. Among things
that stuck&amp;nbsp;were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Optimal posting time is 9am&amp;nbsp;PST&lt;/li&gt;
&lt;li&gt;Writing personal posts is OK&amp;nbsp;sometimes&lt;/li&gt;
&lt;li&gt;Show personal information in the&amp;nbsp;sidebar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last part is expressed in the new design. Overall the redesign was an
exercise in typography, CSS3 features and &lt;a href="http://www.alistapart.com/articles/responsive-web-design/"&gt;responsive layout&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.google.com/webfonts"&gt;Google fonts&lt;/a&gt; is a nice collection of web fonts. I decided to switch from
Myriad (used on borismus.com) to a serif for the main body for a
change. I ultimately went with &lt;a href="http://www.google.com/webfonts/list?family=PT+Serif"&gt;PT Serif&lt;/a&gt;, which seemed like a nice
improvement to Georgia, which is probably my favorite serif web&amp;nbsp;font.&lt;/p&gt;

&lt;p&gt;I used a bunch (too many?) CSS3 features on the new site. Most of these are
CSS box-shadows, gradients, transformations and transitions. I used &lt;a href="http://sass-lang.com/"&gt;SCSS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, following the responsive layout philosophy, I wanted the site to scale well
to a number of different resolutions. For example, the sidebar moves to the end of the 
articles if the window is too narrow. Also, in the &lt;a href="/projects"&gt;projects page&lt;/a&gt;, the number of 
columns of projects is flexible. This is achieved through &lt;a href="http://www.w3.org/TR/css3-mediaqueries/"&gt;CSS media queries&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For posterity, here is the new layout at the time of&amp;nbsp;writing:&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2011/new-design.png" alt="new" /&gt;&lt;/p&gt;

&lt;h2&gt;New&amp;nbsp;domain&lt;/h2&gt;

&lt;p&gt;I also recently bought &lt;a href="http://smus.com"&gt;smus.com&lt;/a&gt; from a fellow in Germany, and will be migrating 
my site there. I'll keep the &lt;a href="http://borismus.com"&gt;borismus.com&lt;/a&gt; blog intact for a while, but will 
place an annoying banner there, and disable commenting. I guess I should also 
switch the feedburner feed to point to smus.com as&amp;nbsp;well.&lt;/p&gt;

&lt;p&gt;Not long ago, I wrote about how switching from webfaction and apache to slicehost
and NGINX greatly &lt;a href="/lightweight-wordpress-on-slicehost/"&gt;enhanced page load time&lt;/a&gt;. Well, this seems to have happened again.
Over the same time period, the average response time of my wordpress instance was 
&lt;strong&gt;934ms&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2011/old-perf.png" alt="old perf" /&gt;&lt;/p&gt;

&lt;p&gt;Over the same time frame, my static blog, hosted on the same machine, responded on 
average in &lt;strong&gt;371ms&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2011/new-perf.png" alt="new perf" /&gt;&lt;/p&gt;

&lt;p&gt;Oh, finally, the site is fully open source &lt;a href="http://github.com/borismus/smus.com"&gt;on github&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;Thanks&lt;/h2&gt;

&lt;p&gt;Thanks to a bunch of&amp;nbsp;people:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sol, Kat and Jon for giving useful design&amp;nbsp;feedback!&lt;/li&gt;
&lt;li&gt;&lt;a href="http://nlevin.com/"&gt;Noah Levin&lt;/a&gt; for awesome CSS3&amp;nbsp;tweaks&lt;/li&gt;
&lt;li&gt;Steve Losh for his &lt;a href="http://stevelosh.com/blog/2010/01/moving-from-django-to-hyde/"&gt;Hyde migration&lt;/a&gt;&amp;nbsp;write-up&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You, for reading this post and continuing to read this blog. Until next&amp;nbsp;time!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/wordpress-to-hyde</feedburner:origLink></entry><entry><title>Chrome Media Keyboard Shortcuts</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/fOLhOwcmdTQ/chrome-media-keys" /><updated>2011-04-01T09:00:35Z</updated><published>2011-04-01T09:00:35Z</published><id>http://smus.com/chrome-media-keys</id><content type="html">
      
&lt;h1&gt;
  Chrome Media Keyboard&amp;nbsp;Shortcuts
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="April 1, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;April 1, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Few people want to synchronize their burgeoning music libraries across
computers (SSDs are small), or even lift it into the clouds with
something like Amazon's Music Cloud (uploading would take forever). As a
result many are saying goodbye to iTunes and moving to web-based
streaming music services like &lt;a href="http://listen.grooveshark.com/"&gt;grooveshark&lt;/a&gt;, &lt;a href="http://www.last.fm"&gt;last.fm&lt;/a&gt;,
&lt;a href="http://thesixtyone.com"&gt;thesixtyone&lt;/a&gt;, &lt;a href="http://rdio.com"&gt;rd.io&lt;/a&gt; etc. This move has a significant UX drawback:
many keyboards come with multimedia keys to control your music player,
but these are useless if you use a web-based music player. This post
addresses this inconvenience with &lt;a href="https://chrome.google.com/extensions/detail/cpgegiegacijlefhjkfodppcefjhgdeo/"&gt;Media Keys&lt;/a&gt;, a Chrome extension
that lets you assign keyboard shortcuts in Chrome control web-based
music players (currently thesixtyone&amp;nbsp;only).&lt;/p&gt;

&lt;h2&gt;How it&amp;nbsp;works&lt;/h2&gt;

&lt;p&gt;The extension relies on two injected &lt;a href="http://code.google.com/chrome/extensions/content_scripts.html"&gt;content scripts&lt;/a&gt;: &lt;a href="https://github.com/borismus/Chrome-Media-Keys/blob/master/key.js"&gt;key.js&lt;/a&gt;, a
keyboard event listener injected into all pages and &lt;a href="https://github.com/borismus/Chrome-Media-Keys/blob/master/t61.js"&gt;player.js&lt;/a&gt;, a
music player controller, injected into the player&amp;nbsp;page. &lt;/p&gt;

&lt;p&gt;Key.js intercepts keyboard commands (ex. the next song key), and if they
match bound music player keyboard shortcuts (ex. next song key =&gt;
change to the next song), it sends a message to player.js, which then
does the right thing (ex. changes to the next song) by simulating mouse
clicks.  Sounds good on paper, but the snag here is that you can't
easily do direct tab-to-tab communication in Chrome extensions, except
possibly through a long-lived port connection. However using long lived
connections doesn't make conceptual sense, since the lifespan of a tab
is relatively&amp;nbsp;short. &lt;/p&gt;

&lt;p&gt;So we go the long way with the help of a &lt;a href="http://code.google.com/chrome/extensions/background_pages.html"&gt;background page&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;On tab load, injected key.js messages the background page to
retrieve media key&amp;nbsp;bindings&lt;/li&gt;
&lt;li&gt;On music player load, injected player.js messages the background
page, reporting its tab ID. The background page &lt;a href="http://code.google.com/chrome/extensions/messaging.html#connect"&gt;opens a Port&lt;/a&gt;
through which to communicate with the player&amp;nbsp;page&lt;/li&gt;
&lt;li&gt;Matching keyboard events on all tabs send messages to the background
page. The background page then relays those messages through the
port to the music&amp;nbsp;player&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's an abridged code sample from the &lt;a href="https://github.com/borismus/Chrome-Media-Keys/blob/master/dispatch.html"&gt;background page&lt;/a&gt;: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;chrome.extension.onRequest.addListener(
  function(request, sender, sendResponse) {
    switch(request.type) {
      case 'command':
        try {
          port.postMessage(request.command);
        } catch(error) {
          if (localStorage['autoload']) {
            chrome.tabs.create({url: SITE_URL});
          }
        }
        sendResponse({});
        break;
      case 'register':
        chrome.tabs.getSelected(null, function(tab) {
          port = chrome.tabs.connect(tab.id);
          sendResponse({});
        });
        break;
    }
  }
);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To summarize, this approach enables a client-side remote control for a
specific web application from any other page. This is potent&amp;nbsp;stuff!&lt;/p&gt;

&lt;h2&gt;Try it&amp;nbsp;out&lt;/h2&gt;

&lt;p&gt;Want to try it out? Media Keys works across all Chrome&amp;nbsp;platforms.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install &lt;a href="http://kevingessner.com/software/functionflip/"&gt;Function Flip&lt;/a&gt; &lt;em&gt;(Mac&amp;nbsp;only)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Check previous, pause/play and next buttons (F6, F7, F8 here) &lt;em&gt;(Mac&amp;nbsp;only)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Install the &lt;a href="https://chrome.google.com/extensions/detail/cpgegiegacijlefhjkfodppcefjhgdeo"&gt;Media Keys&lt;/a&gt;&amp;nbsp;extension&lt;/li&gt;
&lt;li&gt;Open up the extension options, bind the keys and&amp;nbsp;save&lt;/li&gt;
&lt;li&gt;Open up a new page and press the key bound to&amp;nbsp;play&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I made a short screencast showing how to install configure and use the&amp;nbsp;extension.&lt;/p&gt;

&lt;iframe title="YouTube video player" width="600" height="368"
  src="http://www.youtube.com/embed/SrfsnU2gSyI" frameborder="0"
  allowfullscreen&gt;&lt;/iframe&gt;

&lt;h2&gt;Wish&amp;nbsp;list&lt;/h2&gt;

&lt;p&gt;Just to recap, the keyboard shortcut binding pattern described above
injects a script into all tabs, which essentially listens to all key
events. A malicious developer could write a key logger watches username
and password fields, correlates to the current domain and sends
harvested data to some private&amp;nbsp;server. &lt;/p&gt;

&lt;p&gt;There are also several limitations to this keyboard shortcut binding
pattern. It simply won't work in the following&amp;nbsp;cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Chrome is in the&amp;nbsp;background&lt;/li&gt;
&lt;li&gt;Focus inside chrome is not on the page (ex. location&amp;nbsp;bar)&lt;/li&gt;
&lt;li&gt;Chrome is on a special page (ex. new tab page) where content scripts
don't get&amp;nbsp;injected&lt;/li&gt;
&lt;li&gt;The current page intercepts keyboard events and stops propagation
(ex. Google&amp;nbsp;Docs)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Keyboard shortcuts are super important to power users, and Chrome (OS)
surely won't leave us in the dust, so I'm looking forward to helping
address the security risks and practical limitations this approach as a
Chromium project&amp;nbsp;contributor.&lt;/p&gt;

&lt;h2&gt;Share and&amp;nbsp;enjoy&lt;/h2&gt;

&lt;p&gt;One last thing. If you've read my &lt;a href="/chrome-extension-mashups/"&gt;previous post&lt;/a&gt;, you know that I'm a
big fan of &lt;a href="http://thesixtyone.com"&gt;thesixtyone.com&lt;/a&gt; so my initial implementation
works for this service only. Making it work for other music streaming
services is just a matter of creating a customized player.js file for
your favorite music app, and tweaking the manifest to inject the new
player.js into the correct domain. Feel free to fork the 
&lt;a href="https://github.com/borismus/Chrome-Media-Keys"&gt;project on github&lt;/a&gt;.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/chrome-media-keys</feedburner:origLink></entry><entry><title>Chronos: Chrome Browsing Metrics</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/fk2ZeiTkEw4/chronos" /><updated>2011-03-21T09:00:36Z</updated><published>2011-03-21T09:00:36Z</published><id>http://smus.com/chronos</id><content type="html">
      
&lt;h1&gt;
  Chronos: Chrome Browsing&amp;nbsp;Metrics
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="March 21, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;March 21, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Like most people, I'm slowly lifting most of my work into the cloud.
This leads to a lot of time spent in the browser. Just how much, I'm not
really sure. Enter &lt;a href="https://chrome.google.com/extensions/detail/dbgohgmphghmoghphoiaghbopikmmgop/"&gt;Chronos&lt;/a&gt;, a Chrome extension to track how much
time you spend on each domain you visit. Chronos gives a per-day
breakdown of time spent actively browsing. In addition to showing a
graphical summary of domain frequency, you also get a total time spent
in Chrome, and how much time your Chrome spends&amp;nbsp;idle.&lt;/p&gt;

&lt;p&gt;Chronos, named after the Greek god of time, quietly sits and monitors
keyboard and mouse events you generate. If you've been active recently,
the domain you're visiting gets recorded. The data structure that stores
this timing information persists on the client side using localStorage.
This data is never sent to any servers, so your browsing privacy is
preserved. Chronos' visualization is built out of HTML divs. Just for
fun, if you become inactive in Chrome, the Chronos icon in the
extensions toolbar fades&amp;nbsp;out.&lt;/p&gt;

&lt;p&gt;Some features that I would find useful to
add to Chronos revolve around productivity and time&amp;nbsp;management:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Chronos makes it really obvious which sites consume most of your
time. It would make sense to be able to enforce time limits spent on
sites, either by interfacing with an extension like &lt;a href="https://chrome.google.com/extensions/detail/laankejkbhbdhmipfmgcngdelahlfoji"&gt;StayFocusd&lt;/a&gt;,
or by replicating that&amp;nbsp;functionality.&lt;/li&gt;
&lt;li&gt;Many people find it helpful to be reminded to take breaks from
computing, either for RSI purposes or for general productivity.
Chronos already tracks activity levels in Chrome, so it could be
augmented to remind people to take breaks in a way similar to
&lt;a href="http://tech.inhelsinki.nl/antirsi/"&gt;AntiRSI&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Have a great idea to add to Chronos? Let me know, or add it yourself! As
usual, the &lt;a href="https://github.com/borismus/Chronos"&gt;source code&lt;/a&gt; is on&amp;nbsp;github.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/chronos</feedburner:origLink></entry><entry><title>SXSW LEGO MINDSTORMS Hackathon</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/kRXIMrlXJT0/sxsw-mindstorms-hackathon" /><updated>2011-03-17T13:19:00Z</updated><published>2011-03-17T13:19:00Z</published><id>http://smus.com/sxsw-mindstorms-hackathon</id><content type="html">
      
&lt;h1&gt;
  SXSW LEGO MINDSTORMS&amp;nbsp;Hackathon
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="March 17, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;March 17, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;I just returned to San Francisco after having a blast at my second SXSW.
Last time I attended, two years ago, I spent most of my time attending
sessions and networking. This year I was mostly on the other side:
preparing talks, staffing booths and organizing events. This year's
highlight was definitely the LEGO MINDSTORMS hackathon, held as part of
&lt;a href="http://extraordinary-hackers.appspot.com/hackathon.html"&gt;The League of Extraordinary Hackers&lt;/a&gt; event put on by Google and
others, that I helped Chris Messina organize. We modeled the event after
&lt;a href="http://breadpig.com/2011/01/18/hack-club-1-total-success/"&gt;Breadpig's Hack Club&lt;/a&gt;, but scaled it up. The event was so awesome
that here I am blogging about&amp;nbsp;it! &lt;/p&gt;

&lt;p&gt;&lt;a href="/media/images/blog/2011/hackathon.jpg"&gt;&lt;img src="/media/images/blog/2011/hackathon-small.jpg" alt="hackathon image" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At 7pm, 12 teams of 3-6 people began building MINDSTORMS sumo robots
which would then compete in a double elimination tournament. We had
mixed skill levels, from complete novices to people working at National
Instruments, the company that builds the LEGO MINDSTORMS software. Teams
were given 4 hours to build their robots out of two complete NXT sets
and additional LEGO parts. During battle, their autonomous robots were
placed back to back, starting with a 180 degree turn before trying to
push each other out of the ring. The battles started at 11:30pm, and
went on for about an&amp;nbsp;hour.&lt;/p&gt;

&lt;iframe title="YouTube video player" width="600" height="480"
  src="http://www.youtube.com/embed/-oELrCuDrrE" frameborder="0"
  allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;We had a great turnout of spectators and builders on the third floor of
the &lt;a href="http://www.speakeasyaustin.com/"&gt;Speakeasy&lt;/a&gt;. The crowd was engaged the whole time, thanks to the
lively narration of MC &lt;a href="http://twitter.com/tielure"&gt;@tielure&lt;/a&gt;, which is quite surprising, given
the amount of party hopping that usually ensues during SXSW. Here's the
final round (Tiger Blood vs. Beam Me&amp;nbsp;Up):&lt;/p&gt;

&lt;iframe title="YouTube video player" width="600" height="480"
  src="http://www.youtube.com/embed/3EJD6M0zDbw" frameborder="0"
  allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;At the end of the day, we made hundreds of geeks happy, and donated
something on the order of $15,000 of LEGOs to charity. I got to work
with tons of awesome people, including &lt;a href="http://twitter.com/chrismessina"&gt;@chrismessina&lt;/a&gt;, &lt;a href="http://twitter.com/kcpike"&gt;@kcpike&lt;/a&gt;,
&lt;a href="http://twitter.com/StevenCanvin"&gt;@StevenCanvin&lt;/a&gt;, &lt;a href="http://twitter.com/Chrysaora"&gt;@Chrysaora&lt;/a&gt; and many others. The event even got
some good press on &lt;a href="http://mashable.com/2011/03/13/league-of-extraordinary-hackers/"&gt;mashable&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;My only regret is not having my real camera with me, so if you have high
quality footage of the event that I haven't included in this post,
please let me know. Looking forward to next&amp;nbsp;time!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/sxsw-mindstorms-hackathon</feedburner:origLink></entry><entry><title>Soundsourcing: The Sound of the Crowd</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/3Rqhx6q0IAQ/crowdsourcing-sound" /><updated>2011-02-20T10:39:05Z</updated><published>2011-02-20T10:39:05Z</published><id>http://smus.com/crowdsourcing-sound</id><content type="html">
      
&lt;h1&gt;
  Soundsourcing: The Sound of the&amp;nbsp;Crowd
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Feb. 20, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Feb. 20, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;After having dabbled in crowdsourcing research, I wanted to find a
compelling MTurk application around which to build a real service that
would simultaneously be useful, involve creative tasks, and be fun
enough to capture my attention. Generating sound effects seemed to fit
the bill. Creating sounds through imitation or collecting is creative,
fun and (ahem, quality aside) easy due to the ubiquity of microphones.
Enter experimental service &lt;a href="http://soundsourcing.com"&gt;soundsourcing.com&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;First I had to verify some basic questions: Would turkers be willing to
create and submit sound effects? Would their output be sufficiently good
for someone to use in practice? Would it be possible to have some
quality control process to weed out the blatantly bad submissions?
Jumping into experimentation, I asked workers to imitate or record a cow
mooing, paying ten cents for their efforts. After 3 days I had a set of&amp;nbsp;moos:&lt;/p&gt;

&lt;p&gt;&lt;object height="285" width="100%"&gt;&lt;param name="movie"
  value="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Fplaylists%2F527826&amp;amp;show_comments=false&amp;amp;auto_play=false&amp;amp;show_playcount=true&amp;amp;show_artwork=true&amp;amp;color=ff7700"&gt;&lt;/param&gt;&lt;param
  name="allowscriptaccess" value="always"&gt;&lt;/param&gt; &lt;embed
  allowscriptaccess="always" height="285"
  src="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Fplaylists%2F527826&amp;amp;show_comments=false&amp;amp;auto_play=false&amp;amp;show_playcount=true&amp;amp;show_artwork=true&amp;amp;color=ff7700"
  type="application/x-shockwave-flash" width="100%"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/p&gt;

&lt;p&gt;Taking it up a notch, I requested the more obscure sound of a 
&lt;a href="http://soundcloud.com/borismus/sets/turkish-trains-leaving-the-station/"&gt;train leaving the station&lt;/a&gt;. There were many excellent submissions in
the mix, although I suspect that many of them were not created by the
workers but found online despite my empty threat to verify their
originality and reject. My favorite is probably this train, improvised
on&amp;nbsp;harmonica: &lt;/p&gt;

&lt;p&gt;&lt;object height="81" width="100%"&gt;&lt;param name="movie" value="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F9563151"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt; &lt;embed allowscriptaccess="always" height="81" src="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fapi.soundcloud.com%2Ftracks%2F9563151" type="application/x-shockwave-flash" width="100%"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/p&gt;

&lt;p&gt;As usual, the hard problem with crowdsourcing is quality control. I
approach this problem with a rating step after the sound samples have
been submitted, in which turkers select their top three samples from the
sound set (shuffled in random order to avoid &lt;a href="http://www.jstor.org/pss/3151704"&gt;order bias&lt;/a&gt;). Each
instance of such a task can be done cheaply (I tried $0.05), so ordering
the whole set in quality becomes easy. In the future I'd like to write
some JS to verify that the turker in fact listened to all of the
samples. Below is the rating form presented to MTurk&amp;nbsp;users:&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2011/turk-rate.png" alt="image" /&gt; &lt;/p&gt;

&lt;p&gt;When all of the pieces came together and some time remained, I built
&lt;a href="http://soundsourcing.com"&gt;soundsourcing.com&lt;/a&gt; around this concept. I implemented the service
using &lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt; and &lt;a href="http://code.google.com/p/boto/"&gt;Boto&lt;/a&gt;, reusing many concepts from the
&lt;a href="/crowdforge"&gt;CrowdForge&lt;/a&gt; framework. The premise of the service is that people
seeking very specific sound effects that they cannot find on existing
sound effects sites can use soundsourcing to get their custom sounds
built to order. The basic flow of HITs on MTurk looks like&amp;nbsp;this:&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2011/soundsourcing.jpg" alt="image" /&gt; &lt;/p&gt;

&lt;p&gt;Since the cow and train experiments, I have collected &lt;a href="http://soundsourcing.com/set/a-lion-roaring/"&gt;lion roars&lt;/a&gt; and
sword slashes (submissions are in progress at the time of writing).
Please check out &lt;a href="http://soundsourcing.com"&gt;the site&lt;/a&gt;, request a sound sample
and submit some feedback below. Mechanical Turkers, let's make some&amp;nbsp;noise!!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/crowdsourcing-sound</feedburner:origLink></entry><entry><title>CrowdForge: Crowdsourcing Complex Tasks</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/ZHSJuv4tMOo/crowdforge" /><updated>2011-02-02T06:30:23Z</updated><published>2011-02-02T06:30:23Z</published><id>http://smus.com/crowdforge</id><content type="html">
      
&lt;h1&gt;
  CrowdForge: Crowdsourcing Complex&amp;nbsp;Tasks
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Feb. 2, 2011"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Feb. 2, 2011&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Work marketplaces like MTurk are great for accomplishing small, well
defined nuggets of work, such as labeling images and transcribing audio,
but terrible for many more complex and labor intensive real world tasks.
Over the last year, Robert Kraut, Niki Kittur and I formalized a general
process of solving such complex problems using MTurk. We proposed three
basic types of tasks and explored them in two neat experimental
applications. To facilitate these experiments, I implemented CrowdForge,
a Django framework that takes output from MTurk HITs and uses it to
create new MTurk&amp;nbsp;HITs. &lt;/p&gt;

&lt;p&gt;Solving complex problems on MTurk has always involved partitioning the
complex task into simpler sub-tasks.  CastingWords, one of the most
popular MTurk requesters, transcribes long audio recordings by splitting
them into overlapping segments, distributing the work among workers,
performing quality control and then recombining the transcription
fragments. CrowdForge formalizes this approach and takes it to the next
level. CrowdForge proposes the following task breakdown, roughly
inspired by the MapReduce programming&amp;nbsp;paradigm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;partition&lt;/strong&gt; tasks split a problem into sub-problems (one to&amp;nbsp;many)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;map&lt;/strong&gt; tasks solve a small unit of work (one to&amp;nbsp;one)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;reduce&lt;/strong&gt; tasks combine multiple results into one (many to&amp;nbsp;one)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2011/crowdforge-simple.png" alt="image" /&gt; &lt;/p&gt;

&lt;p&gt;CastingWords uses human intelligence only for their map tasks, which
consist of transcribing speech samples to text. Their partition task may
involve an algorithm which seeks natural breaks in speech audio samples,
while the reduce task may involve programmatic stitching of audio
samples. For sufficiently complex cases, however, algorithms may be
inadequate, and the partitioning and reduction require human
intelligence. Here are some experiments that illustrate this&amp;nbsp;idea:&lt;/p&gt;

&lt;h2&gt;Writing&amp;nbsp;Articles&lt;/h2&gt;

&lt;p&gt;In the first experiment, turkers generated encyclopedia-style articles
on a given subject. The approach I took was to first generate an article
outline using an partition task, then for each heading in the outline,
to collect facts on the topic, next to combine these facts into a
paragraph, finally merging all of the paragraphs into a final article.
The following diagram illustrates this process for an article on New
York&amp;nbsp;City: &lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2011/crowdforge-article.png" alt="image" /&gt; &lt;/p&gt;

&lt;p&gt;In one incarnation of this experiment, we used this approach to create
five articles about New York City. Articles cost an average of $3.26 to
produce, required an average of 36 subtasks or HITs, included an average
of 5.3 topics per article and consisted of an average of 658 words. As a
comparison baseline, we created eight HITs which each requested one
worker to write the full article, paying $3.26, the same amount required
for the collaboratively written&amp;nbsp;articles. &lt;/p&gt;

&lt;p&gt;We then evaluated the quality of all articles by asking a new set of
workers to each rate a single article based on use of facts, spelling
and grammar, article structure, and personal preference. On average the
articles produced by the group were of rated on par with the Simple
Wikipedia article on the same topic, and higher than those produced
individually. See the paper for gritty details on the&amp;nbsp;stats.&lt;/p&gt;

&lt;h2&gt;Product&amp;nbsp;Comparisons&lt;/h2&gt;

&lt;p&gt;We did another experiment to prove the generality of the approach. We
used CrowdForge to create purchase decision matrices to assist consumers
looking to buy a new car. Given a short description of a consumer need,
we created two partition tasks: one to decide which cars might be
appropriate to consider, and one to decide which features the consumer
cares most about. This double partition resulted in an empty product
comparison chart. Each cell in the chart then spawned a map task to
collect related facts. Next, these facts are reduced into a sentence,
resulting in a product comparison chart. Here's an&amp;nbsp;excerpt: &lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2011/crowdforge-purchase.png" alt="image" /&gt;&lt;/p&gt;

&lt;p&gt;The entire task was completed in 54 different HITs for a total cost of
$3.70. When we tried to compare to the individual case, we had no
success getting individuals to generate a similar product comparison
chart, even when offering more money than we paid the entire&amp;nbsp;group.&lt;/p&gt;

&lt;h2&gt;Sweet&amp;nbsp;Applications&lt;/h2&gt;

&lt;p&gt;MTurk is a one of my favorite tools for doing &lt;a href="http://www.quora.com/What-are-the-most-creative-uses-of-Amazon-s-Mechanical-Turk"&gt;creative and novel&lt;/a&gt;
projects. As illustrated, applying human intelligence to reduce and
partition tasks, we can solve a new set of interesting problems. But
there is much more to explore! For example, imagine collaborative
drawing assignments in which a worker sketches out a picture, and
subsequent workers refine the original picture by drawing sub-pictures
or specific objects. Imagine requesting a trip plan and getting a
researched day-by-day itinerary of what to see and do. Imagine
partitioning a Java class into methods, outsourcing the implementation
and unit test&amp;nbsp;implementations. &lt;/p&gt;

&lt;p&gt;CrowdForge is written as a Django application that communicates with
MTurk servers using the &lt;a href="http://code.google.com/p/boto/"&gt;Boto interface&lt;/a&gt;, which is a Python framework
that encapsulates the Amazon Web Services API. CrowdForge regularly
polls MTurk and fires notifications whenever interesting things happen
(a new result comes in for a HIT, all HITs of a certain type are
finished, a HIT is expired or complete). CrowdForge can manage many
flows, which encapsulate code that determines how to respond to these
events. For example, the article writing flow knows to create fact
collection HITs for each outline topic once the outline is submitted. So
to solve a new kind of complex problem, you would extend CrowdForge with
a custom flow, like the&amp;nbsp;following: &lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2011/crowdforge-complex.png" alt="image" /&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="dquo"&gt;"&lt;/span&gt;But Boris", you say, "how can I get my hands on this CrowdForge
framework of yours?" Indeed, we just &lt;a href="https://github.com/borismus/CrowdForge"&gt;released it&lt;/a&gt; for non-commercial
use under &lt;a href="http://creativecommons.org/licenses/by-nc/3.0/"&gt;the Creative Commons license&lt;/a&gt;. Let me know if you have any
questions, comments or interest in collaborating or maintaining the&amp;nbsp;framework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update (February 2011):&lt;/strong&gt; CrowdForge was covered in &lt;a href="http://www.newscientist.com/article/mg20927985.800-silicon-supervisor-gets-the-job-done-online.html"&gt;NewScientist&lt;/a&gt; and in a
&lt;a href="http://www.cmu.edu/homepage/society/2011/winter/crowdforge.shtml"&gt;CMU&amp;nbsp;article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update (November 2011)&lt;/strong&gt;: The &lt;a href="/media/images/blog/2011/crowdforge-uist-11.pdf"&gt;full paper&lt;/a&gt; was &lt;a href="http://smustalks.appspot.com/crowdforge-11/"&gt;presented&lt;/a&gt; at
&lt;a href="http://www.acm.org/uist/"&gt;UIST 2011&lt;/a&gt; in Santa&amp;nbsp;Barbara.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/crowdforge</feedburner:origLink></entry><entry><title>KML Tours in Google Earth</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/KOpcQKXmibQ/kml-tours-google-earth" /><updated>2010-12-25T16:17:42Z</updated><published>2010-12-25T16:17:42Z</published><id>http://smus.com/kml-tours-google-earth</id><content type="html">
      
&lt;h1&gt;
  KML Tours in Google&amp;nbsp;Earth
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Dec. 25, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Dec. 25, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Over this last year and a half, I spent a lot of time in airports,
flying nearly 150,000 km (queue environmental angst). I lived in Europe
and both coasts of the US, somehow getting a masters from CMU in the
end. For posterity, I wanted to digitally summarize this extensive
globetrotting experience, but found no adequate applications. It was an
experience worth celebrating, and how better to celebrate than by
writing some code...? So I wrote &lt;a href="http://thattrip.appspot.com/"&gt;an application demo&lt;/a&gt; that renders
travel adventures as a tour in Google&amp;nbsp;Earth. &lt;/p&gt;

&lt;p&gt;First you fill in a pretty silly madlib describing your&amp;nbsp;trip: &lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2010/madlib.png" alt="image" /&gt; &lt;/p&gt;

&lt;p&gt;Then, you can view your trip in the browser using the [Google Earth
plugin][], or download the tour and open it in the Google Earth
application. Each trip gets a unique URL that can be shared with others.
Here is a youtubified summary of my recent travels generated by this&amp;nbsp;app:&lt;/p&gt;

&lt;iframe title="YouTube video player" width="600" height="480"
  src="http://www.youtube.com/embed/N1y8C_w-7Uw" frameborder="0"
  allowfullscreen&gt;&lt;/iframe&gt;

&lt;h2&gt;How it&amp;nbsp;works&lt;/h2&gt;

&lt;p&gt;The madlib provides a list of places and transportation modes, the
places resolving to (lat, long) coordinates, and the transportation
modes affecting the transition effects between places. Airplane
transitions are shown with the camera oriented north, in a bird's eye
view, while ground transport transitions are more complex, facing in the
direction of travel and changing pitch angle. The application builds KML
in JavaScript and eventually loads it all in a Google Earth&amp;nbsp;plugin:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// Build the KML
var helper = new KMLHelper();
helper.processData(data.places, function() {
  // Generate KML tour for this
  data.kml = helper.kml();
  data.distance = helper.distance();

  var json = JSON.stringify(data);
  // Save the new data to the database
  $.ajax({...})
});

// Eventually render the KML (ge is a Google Earth instance)
$.getJSON('/trips/' + id + '/', function(data) {
  var kmlObject = ge.parseKml(data.kml);
  ge.getFeatures().appendChild(kmlObject);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Place-to-place transition effects are achieved by using &lt;a href="http://code.google.com/apis/kml/documentation/touring.html"&gt;KML Tours&lt;/a&gt;,
which provide powerful camera control requiring some computation, like
getting the midpoint between (lat, long) pairs. Math for midpoint and
other common geodesy operations are &lt;a href="http://www.movable-type.co.uk/scripts/latlong.html"&gt;well documented&lt;/a&gt; by Chris Veness,
who also provides an implementation in the form of a
&lt;a href="http://www.movable-type.co.uk/scripts/latlon.js"&gt;handy JavaScript library&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;In addition to destinations, travel paths and transitions, this demo
uses the &lt;a href="http://code.google.com/apis/picasaweb/overview.html"&gt;picasaweb API&lt;/a&gt; to get public picasaweb images as
GroundOverlays near places you visited. While it's possible to get a
user's photos tagged with a tag, there is, rather bizarrely, no way to
get a specific user's images tagged with a given&amp;nbsp;location. &lt;/p&gt;

&lt;p&gt;As usual, all of the code is available on &lt;a href="https://github.com/borismus/That-Trip"&gt;the github&lt;/a&gt;. It's running
live on &lt;a href="http://thattrip.appspot.com/"&gt;thattrip.appspot.com&lt;/a&gt;, so try it&amp;nbsp;out.&lt;/p&gt;

&lt;h2&gt;Observations&lt;/h2&gt;

&lt;p&gt;The Google Earth plugin provides what is probably the richest map view
available on the web, but it is not without drawbacks. The mere fact
that it's a plugin immediately shrinks the possible user base of any app
that relies it and adds to load time. There's no way to pre-cache the
data that will be shown on a tour, causing a lot of chunky terrain
during quick transitions. While KML is rich and makes it possible to
embed images as ground and photo overlays, the web at large has far more
options to handle images, through CSS transformations and animations or
by embedding in a canvas or SVG&amp;nbsp;element. &lt;/p&gt;

&lt;p&gt;This demo is very geo-centric, telling a story almost exclusively about
where you went, with the images added as an afterthought. In retrospect,
a more compelling story is one about what you personally saw, shown in
travel photos, with a geodesy cherry on top. Stay tuned for future&amp;nbsp;work...&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/kml-tours-google-earth</feedburner:origLink></entry><entry><title>Tweetbacks in JavaScript</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/6mRMJdLB0cQ/tweetbacks-in-javascript" /><updated>2010-12-16T12:58:04Z</updated><published>2010-12-16T12:58:04Z</published><id>http://smus.com/tweetbacks-in-javascript</id><content type="html">
      
&lt;h1&gt;
  Tweetbacks in&amp;nbsp;JavaScript
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Dec. 16, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Dec. 16, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Every new post on this blog gets an unsolicited &lt;a href="http://en.wikipedia.org/wiki/Pingback"&gt;pingback&lt;/a&gt; from
&lt;a href="http://topsy.com"&gt;topsy&lt;/a&gt;, a service that tracks which users mentioned the post on
twitter (known informally as &lt;a href="http://tomsucks.wordpress.com/2008/05/27/free-idea-tweetback/"&gt;tweetbacks&lt;/a&gt;). On one hand, topsy is a
parasite, using sites like mine to rise in search engine rankings, but
on the other, it satisfies this blogger's curiosity to learn who reads
and enjoys my posts enough to tweet about them. In this experiment, I've
exposed tweetbacks directly on this blog using topsy's convenient 
&lt;a href="http://otter.topsy.com"&gt;JSONP API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The implementation is entirely in JavaScript, consisting of two
scripts. The first is on the main post listing, showing the number of
tweets for each post using &lt;code&gt;http://otter.topsy.com/stats.js&lt;/code&gt;. The second
is on the post page itself, showing the tweets posted in response to the
post using &lt;code&gt;http://otter.topsy.com/trackbacks.js&lt;/code&gt;. Both scripts execute
after the page loads, so the only impact on page load time is the extra
kilobyte of JavaScript&amp;nbsp;code. &lt;/p&gt;

&lt;p&gt;There are good reasons to use topsy over &lt;a href="http://search.twitter.com"&gt;twitter search&lt;/a&gt;, even though
at first glance, they both provide similar&amp;nbsp;information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Twitter search doesn't index old&amp;nbsp;tweets&lt;/li&gt;
&lt;li&gt;Topsy distinguishes influential tweets (more on this&amp;nbsp;later)&lt;/li&gt;
&lt;li&gt;JSONP API&amp;nbsp;support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's important not to overwhelm readers by showing too many tweets.
Popular blog posts can have hundreds or even thousands of tweets, and
showing all of them at once is a bad idea. Topsy associates an influence
value with each twitter user, giving them a certain weight, making it
easier to decide which tweets to show, and which to hide. My
implementation shows at most N tweets; if there are over N total tweets,
it shows at most N 'influential' tweets, as per topsy's&amp;nbsp;definition.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var MAX_TWEETS = 10;
var BASE = 'http://otter.topsy.com/trackbacks.js?callback=?&amp;amp;perpage=' + 
  MAX_TWEETS;
var ALL = BASE + '&amp;amp;url=';
var INFL = BASE + '&amp;amp;infonly=1&amp;amp;url=';

function getTweets(url) {
  $.getJSON(ALL + url, function(data) {
    var response = data.response;
    if (response.total &amp;gt; MAX_TWEETS) {
      $.getJSON(INFL + url, function(infl) {
        processTweetList(infl.response.list);
        var count = (infl.response.total &amp;gt; MAX_TWEETS ? MAX_TWEETS : infl.response.total);
        updateTweetCount(count, response.total);
      });
    } else if (response.total &amp;gt; 0) {
      processTweetList(response.list);
      updateTweetCount(response.total);
    }
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I also urlify the
tweet, converting all of the URLs and @mentions into &lt;a\&amp;gt; elements using
this function, I found the first regular expression somewhere on the
internet – I'd never write such a&amp;nbsp;beast. &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var URL_RE = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&amp;amp;@#\/%?=~_|!:,.;]*[-A-Z0-9+&amp;amp;@#\/%=~_|])/ig;
var TWEET_RE = /@([A-Za-z0-9_]+)/g;

function urlify(text) {
  return text.replace(URL_RE,"&amp;lt;a href='$1'&amp;gt;$1&amp;lt;/a&amp;gt;").
              replace(TWEET_RE, "&amp;lt;a href='http://twitter.com/$1'&amp;gt;@$1&amp;lt;/a&amp;gt;");
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The approach I took has some major advantages to over the &lt;a href="http://yoast.com/wordpress/tweetbacks/"&gt;tweetback plugin&lt;/a&gt; 
for&amp;nbsp;wordpress:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It works... the wordpress plugin&amp;nbsp;doesn't&lt;/li&gt;
&lt;li&gt;No wordpress comment pollution since no wordpress comments are&amp;nbsp;created&lt;/li&gt;
&lt;li&gt;No server-side load since all comments are fetched in&amp;nbsp;JS&lt;/li&gt;
&lt;li&gt;No wordpress required. Other blog engines or static sites work just
as&amp;nbsp;well&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A drawback of this approach is that it takes some time to run the JS,
which changes the DOM after the initial page load, resulting in a
jarring experience. By using a fade effect to make twitter information
appear gradually, I try to mitigate this&amp;nbsp;problem. &lt;/p&gt;

&lt;p&gt;I conclude with a shameless plug: tweetbacks.js is running on this blog,
so try it out by posting a tweet referring to this post's URL, and it
should appear in the list below, or if there are more than 10 tweets, at
least increment the count. If you find this concept interesting and
would like to run it on your site, let me know and I'll pack
tweetbacks.js up into a jQuery plugin or something. Thanks for&amp;nbsp;reading! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I no longer use this code, in favor of the official twitter
button. For posterity, here are the &lt;a href="/x/topsy-tweetback.zip"&gt;topsy-twitterized files&lt;/a&gt;.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/tweetbacks-in-javascript</feedburner:origLink></entry><entry><title>WebIntent, an Android PhoneGap Plugin</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/_7Z8pLSFmZc/android-phonegap-plugins" /><updated>2010-11-25T18:42:26Z</updated><published>2010-11-25T18:42:26Z</published><id>http://smus.com/android-phonegap-plugins</id><content type="html">
      
&lt;h1&gt;
  WebIntent, an Android PhoneGap&amp;nbsp;Plugin
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Nov. 25, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Nov. 25, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="http://phonegap.com/"&gt;PhoneGap&lt;/a&gt; is a very useful cross-platform web application wrapper
that lets developers package web applications into native apps. It also
lets web apps access functionality traditionally only available in
native apps. This how-to is about exposing additional Android
functionality to the web with PhoneGap plugins. &lt;a href="http://developer.android.com/reference/android/content/Intent.html"&gt;Intents&lt;/a&gt; are a
fundamental part of the Android ecosystem, allowing a sort of
message-passing mechanism between applications, but they are not exposed
to web applications. The sample Android plugin I wrote is called
WebIntent, which lets you create a first class Android applications in&amp;nbsp;JavaScript.&lt;/p&gt;

&lt;h2&gt;WebIntent&lt;/h2&gt;

&lt;p&gt;Firstly, the WebIntent plugin is a means of creating Android activities
via Intents. With just four parameters, &lt;code&gt;action, url, type, extras&lt;/code&gt;,
it's possible to get a lot of mileage from the Android OS. For example,
you can send&amp;nbsp;email: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Android.sendEmail = function(subject, body) { 
  var extras = {};
  extras[WebIntent.EXTRA_SUBJECT] = subject;
  extras[WebIntent.EXTRA_TEXT] = body;
  window.plugins.webintent.startActivity({ 
      action: WebIntent.ACTION_SEND,
      type: 'text/plain', 
      extras: extras 
    }, 
    function() {}, 
    function() {
      alert('Failed to send email via Android Intent');
    }
  ); 
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or, you can load Google&amp;nbsp;Maps: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Android.showMap = function (address) {
  window.plugins.webintent.startActivity({
    action: WebIntent.ACTION_VIEW,
    url: 'geo:0,0?q=' + address,
  }, function () {}, function () {
    alert('Failed to open URL via Android Intent');
  });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Secondly, the plugin lets you react when your PhoneGap
application gets invoked with certain intents. To do this, you need to
setup correct intent-filters in the AndroidManifest.xml. For example, to
respond to ACTION_SEND, something like the following should appear in
the&amp;nbsp;manifest: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;intent-filter&amp;gt; 
  &amp;lt;action android:name="android.intent.action.SEND" /&amp;gt;
  &amp;lt;category android:name="android.intent.category.DEFAULT" /&amp;gt;
  &amp;lt;data android:mimeType="text/plain" /&amp;gt;
&amp;lt;/intent-filter&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, in the JavaScript, you can check what extras were specified during&amp;nbsp;invocation:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// deviceready is PhoneGap's init event
document.addEventListener('deviceready', function () {
  window.plugins.webintent.getExtra(WebIntent.EXTRA\_TEXT, function (url) {
    // url is the value of EXTRA_TEXT 
  }, function() {
    // There was no extra supplied.
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This lets you respond to Intents without writing native code. The plugin
code is available for your use and/or perusal on &lt;a href="https://github.com/borismus/phonegap-plugins/tree/master/Android/WebIntent/"&gt;my github&lt;/a&gt;. To install
the plugin, move &lt;a href="https://github.com/borismus/phonegap-plugins/blob/master/Android/WebIntent/webintent.js"&gt;webintent.js&lt;/a&gt; to your project's www folder and include a
reference to it in your html files. Create a folder called "borismus" within
your project's src/com/ folder and move &lt;a href="https://github.com/borismus/phonegap-plugins/blob/master/Android/WebIntent/WebIntent.java"&gt;WebIntent.java&lt;/a&gt; into&amp;nbsp;it.  &lt;/p&gt;

&lt;h2&gt;Writing Android PhoneGap&amp;nbsp;Plugins&lt;/h2&gt;

&lt;p&gt;The PhoneGap project provides a &lt;a href="http://blogs.nitobi.com/joe/2009/12/17/introducing-ponygap-phonegap-plugins-for-android/"&gt;plugin architecture&lt;/a&gt;, which isn't very
well documented. Perhaps as a result, there are very few Android plugins
(see &lt;a href="https://github.com/phonegap/phonegap-plugins/tree/master/Android/"&gt;the github&lt;/a&gt;). There are two parts of an Android plugin: a native
Java class that extends &lt;code&gt;com.phonegap.api.Plugin&lt;/code&gt;, and a JavaScript wrapper
for that Java class. The JavaScript wrapper registers the plugin with the
PhoneGap plugin manager. A plugin API is defined in JavaScript, and exposed
via &lt;code&gt;window.plugins.myplugin&lt;/code&gt;. Calling the API is done as&amp;nbsp;follows: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;window.plugins.webintent.foo({ arg1: 'val1', arg2: 'val2', // etc });
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The plugin API is defined in the &lt;code&gt;MyPlugin&lt;/code&gt; JavaScript&amp;nbsp;class:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var MyPlugin = function () {};
MyPlugin.prototype.foo = function (params, success, fail) {
  return PhoneGap.exec(success, fail, 'MyPlugin', 'startActivity', [params]);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The plugin needs to be registered with PhoneGap before it can be&amp;nbsp;used. &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;PhoneGap.addConstructor(function () {
  // Creates window.plugins.myplugin, an instance of MyPlugin
  PhoneGap.addPlugin('myplugin', new MyPlugin()); 
  // Binds MyPlugin to the Java class com.example.MyPlugin 
  PluginManager.addService('MyPlugin', 'com.example.MyPlugin');
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, there needs to be a Java class that actually implements the desired
behavior. The execute method takes an action parameter, which is the name of
the function (foo in this example), and an array of&amp;nbsp;arguments: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public class MyPlugin extends Plugin {
  public PluginResult execute(String
  action, JSONArray args, String callbackId) {
    if (action.equals("foo")) {
      // Implementation 
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This architecture is the key to unlock any Android functionality to your
PhoneGap-wrapped mobile web application. One of huge benefits of PhoneGap
and mobile web is that it's a cross-platform solution. I admit that writing
platform-specific plugins seems counter-productive to this end. However
until the mobile web gets the love it deserves (via extra sweet mobile
browsers), there will be a place for platform-specific PhoneGap plugins to
make web applications fit better into native mobile&amp;nbsp;platforms.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/android-phonegap-plugins</feedburner:origLink></entry><entry><title>jQuery Mobile Hacker News</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/LAn1nhRI1y0/jquery-mobile-hacker-news" /><updated>2010-11-17T08:00:25Z</updated><published>2010-11-17T08:00:25Z</published><id>http://smus.com/jquery-mobile-hacker-news</id><content type="html">
      
&lt;h1&gt;
  jQuery Mobile Hacker&amp;nbsp;News
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Nov. 17, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Nov. 17, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;I'm about to embark on a 3-week heads-down coding spree to finish off
the final project of my masters. I convinced my team and our clients
that it's a good idea to use jQuery Mobile instead of native Android to
implement our data-centric application, so I've been playing with it
quite a bit over the last week. jQM is still in Alpha 2, so to get my
feet wet, I decided to write a demo app before starting the final
project. After stumbling over a few kinks and rough edges, a weekend of
coding yielded a &lt;a href="/x/hackernews/"&gt;mobile Hacker News client&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2010/hackernews.png" alt="image" /&gt;&lt;/p&gt;

&lt;h2&gt;Loading&amp;nbsp;JavaScript&lt;/h2&gt;

&lt;p&gt;jQuery Mobile is firmly rooted in the progressive enhancement philosophy
of web application development. In practice, this means that the simple
markup that you write in it is processed by the framework's JavaScript
and transformed into much more complex and JS-enhanced markup. In
particular, jQM rewrites &lt;code&gt;&amp;lt;a href="page.html"&amp;gt;&amp;lt;/a&amp;gt;&lt;/code&gt; into an element that
loads the contents of the page via an AJAX request. Unfortunately, CSS
and JavaScript referenced by &lt;code&gt;page.html&lt;/code&gt; is not&amp;nbsp;loaded. &lt;/p&gt;

&lt;p&gt;To work around this, I've been using a makeshift JS loader which latches
on to the jQM pageshow event so that whenever an HTML page loads, the
corresponding JavaScript file also loads. To prevent the JS loader from
loading already-loaded JavaScript whenever a page is reloaded, it
employs a cache. Still, the JS needs to be triggered when the page is
loaded, so there's a way to bind a page load to a function. Here's some
sample usage of the&amp;nbsp;ScriptCache:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// Create a new ScriptCache in the global scope
var scriptCache = new ScriptCache();
// Now when page.html loads, page.js will load

// Also, each page can register with the scriptCache
scriptCache.onPageLoad('page.html', pageInitFunction);

// Each page.js looks like this:
(function() {
  var init = function() {...};

  // Whenever this page is loaded, call init
  scriptCache.onPageLoad('page.html', init);
  // Call init the first time it's loaded too
  init();
})();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This approach was inspired by a discussion on the &lt;a href="http://forum.jquery.com/topic/links-don-t-load-scripts"&gt;jQuery Forum&lt;/a&gt;, and
I really hope that there will be a better answer from readers or the jQM
developers. The &lt;a href="https://github.com/borismus/jQuery-Mobile-Hacker-News/blob/master/assets/www/scriptcache.js"&gt;ScriptCache loader is on github&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Passing&amp;nbsp;Parameters&lt;/h2&gt;

&lt;p&gt;Another issue I came across was parameter passing between pages. The jQM
Hacker News client needs to pass item IDs from the main page listing to
item pages. Unfortunately jQuery Mobile seems to have no provision for
doing this. A couple approaches come to mind. One way is through a
global variable parameter passing convention (possible since jQuery
pages aren't navigated to, but loaded with AJAX). The way I'm using is
through GET parameters after the hash. Thus the URL to an item page
looks like this: http://example.com/index.html#item.html?id=82831.
However, the pageshow event fires &lt;em&gt;just slightly&lt;/em&gt; before the
window.location is updated with the requested URL. I'm currently working
around this with a &lt;code&gt;setTimeout&lt;/code&gt;, but this is clearly unacceptable. This
issue is being &lt;a href="https://github.com/jquery/jquery-mobile/issues#issue/450/comment/543394"&gt;discussed on github&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;There's also an issue passing parameters via the jQM changePage call:
&lt;code&gt;$.mobile.changePage('page.html?key=value');&lt;/code&gt;. The parameters are simply
ignored. Thus I was forced to use &lt;code&gt;window.location.href +=
'#page.html?key=value';&lt;/code&gt;, which only works if your window.location.href
is the main application&amp;nbsp;page.&lt;/p&gt;

&lt;h2&gt;Hacker&amp;nbsp;News&lt;/h2&gt;

&lt;p&gt;Using the workarounds outlined above, I wrote an application that lets
you read and post to &lt;a href="http://news.ycombinator.com"&gt;Hacker News&lt;/a&gt;, a startup-oriented online
community run by &lt;a href="http://ycombinator.com"&gt;Y Combinator&lt;/a&gt;. It uses the &lt;a href="http://api.ihackernews.com"&gt;third party HN API&lt;/a&gt;
provided by &lt;a href="http://twitter.com/ronnieroller"&gt;@ronnieroller&lt;/a&gt;, who has been very responsive on twitter
(thanks!). For fetching posts and comments, the application relies on
the JSONP methods of this API. It doesn't currently permit authenticated
posting and voting on&amp;nbsp;HN. &lt;/p&gt;

&lt;p&gt;For commenting and submitting links, I embed actual Hacker News post
pages in an iframe, working around the lack of support and avoiding
trust issues. Unfortunately the &lt;a href="http://code.google.com/p/android/issues/detail?id=12558"&gt;Android browser has a bug&lt;/a&gt; which
forces iframes to resize to the width of their contained textarea if one
exists in the embedded page. This causes the width of the page to grow
which forces jQuery Mobile to relayout. The reference implementation
(iOS) does not suffer from this&amp;nbsp;issue. &lt;/p&gt;

&lt;p&gt;I'd be glad to get your feedback on this app. If you want to read the
code or fork the project, it's available &lt;a href="https://github.com/borismus/jQuery-Mobile-Hacker-News/tree/master/assets/www/"&gt;on github&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;I've packed this application with PhoneGap, and currently use it as the
HN reader on my phone. Existing HN applications on Android don't provide
share intent handlers (to share from the browser or your RSS reader).
This app provides share intent handling through WebIntents, an Android
PhoneGap plugin that I've been working on. More on that next post, so
stay&amp;nbsp;tuned!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/jquery-mobile-hacker-news</feedburner:origLink></entry><entry><title>PhoneGap Games in the Android Market</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/1IQEiH50SYc/phonegap-games-android-market" /><updated>2010-11-11T15:38:48Z</updated><published>2010-11-11T15:38:48Z</published><id>http://smus.com/phonegap-games-android-market</id><content type="html">
      
&lt;h1&gt;
  PhoneGap Games in the Android&amp;nbsp;Market
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Nov. 11, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Nov. 11, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;A little while ago I took an existing game called &lt;a href="http://lostdecadegamesapp.appspot.com/"&gt;Onslaught!&lt;/a&gt; by
&lt;a href="http://blog.lostdecadegames.com/"&gt;Lost Decade Games&lt;/a&gt; and hacked it to use onscreen controls instead of
the keyboard so that it could be enjoyed on mobile phones. This was
intended as a proof of concept and not a polished product as I
encountered many technical show-stoppers, described in the 
&lt;a href="/mobile-html-games/"&gt;previous blog post&lt;/a&gt;. I've been itching to experiment with &lt;a href="http://www.phonegap.com/"&gt;PhoneGap&lt;/a&gt; apps
in the Android Market, so despite obvious issues, I decided to pack up my
Onslaught! fork and upload it to the&amp;nbsp;market. &lt;/p&gt;

&lt;p&gt;I expected there to be many gameplay issues with varying screen sizes
and form factors. I strongly suspected my use of the target-densityDpi
would break on non-Nexus One handsets. Indeed, my suspicions were
confirmed when, after a nominal $25 fee and a short setup, I uploaded
the PhoneGap-created .apk to the&amp;nbsp;Market.&lt;/p&gt;

&lt;h2&gt;Results&lt;/h2&gt;

&lt;p&gt;I've never published any application to any application store before. I
was surprised to see Onslaught! available in the market within seconds
of my submission, with a surge of new users and comments piling in
nearly immediately. Apparently the experience is particularly horrible
on the Galaxy S and it doesn't work at all on HTC Hero. The day I
unleashed Onslaught! unto the Market, I was contacted by someone from
&lt;a href="http://zeemote.com/"&gt;Zeemote&lt;/a&gt;, who politely suggested that the game "could possibly
benefit from the use of a joystick for improved control", and promptly
&lt;strong&gt;sent me a Zeemote&lt;/strong&gt; to experiment with. I haven't found the time yet,
but thanks Zeemote, that was pretty sweet of you – nobody's ever sent me
free stuff&amp;nbsp;before! &lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2010/onslaught_stats.png" alt="image" /&gt; &lt;/p&gt;

&lt;p&gt;Obviously this is a pretty embarrassing state of affairs, and I, like
any slightly self respecting developer decided to remove the app from
the market to hide the evidence! I also received a very friendly
&lt;strong&gt;cease-and-desist&lt;/strong&gt; style letter from the good folks at LDG (this is
one of those times when the "ask for forgiveness, not permission"
approach has its drawbacks). Needless to say, I gladly pulled the game
and apologized profusely to the developers. For posterity, the 
&lt;a href="/x/onslaught/onslaught.apk"&gt;APK is still available&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;PhoneGap&lt;/h2&gt;

&lt;p&gt;Creating an Android PhoneGap application out of a web application is
pretty simple. It's a matter of following a few steps outline on 
&lt;a href="http://wiki.phonegap.com/w/page/16494774/Getting-started-with-Android-PhoneGap-in-Eclipse"&gt;this documentation page&lt;/a&gt;. The process varies slightly for each platform and
would surely become painful and tedious for developers supporting
multiple platforms. Luckily the PhoneGap folks will soon have just the
thing to address this issue. &lt;a href="http://build.phonegap.com/start"&gt;PhoneGap Build&lt;/a&gt; is a service that
promises to package and compile web apps for a variety of platforms.
Looking forward to trying it&amp;nbsp;out!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/phonegap-games-android-market</feedburner:origLink></entry><entry><title>Chrome Extension for thesixtyone</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/yRTVpQNa8q4/chrome-extension-mashups" /><updated>2010-10-31T08:09:10Z</updated><published>2010-10-31T08:09:10Z</published><id>http://smus.com/chrome-extension-mashups</id><content type="html">
      
&lt;h1&gt;
  Chrome Extension for&amp;nbsp;thesixtyone
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Oct. 31, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Oct. 31, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;It's a little late to join the Chrome Extension writing party, and
unfortunately &lt;a href="http://www.readwriteweb.com/archives/chrome_web_store_delayed_until_december.php"&gt;still too early&lt;/a&gt; for the Chrome Web Store launch.
Timing issues aside, &lt;a href="https://chrome.google.com/extensions/detail/hghoinoackbjefgfkbgnkjknmneajoof"&gt;here's an extension&lt;/a&gt; that presents
thesixtyone.com in a smaller, simpler interface, effectively saving a
tab in Chrome and removing all of the (useless to me) social game
mechanics from thesixtyone. This post walks through some technical
details of the implementation, message passing between pages of an
extension, JavaScript injection and DOM event&amp;nbsp;generation. &lt;/p&gt;

&lt;p&gt;Wrapping a web application in an extension may sound deceptively easy:
just load the website in an iframe in the background (via 
&lt;a href="https://code.google.com/chrome/extensions/background_pages.html"&gt;background pages&lt;/a&gt;), and then control the website from the background page
via commands from the popup page that appears when you click the extension icon
in the toolbar. Not so fast! The key issue with this approach is the control
part. For good reason, browser security disallows executing JavaScript in an
embedded iframe if the src is another domain. The solution to this problem is
two&amp;nbsp;fold:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Chrome extensions can &lt;a href="https://code.google.com/chrome/extensions/content_scripts.html"&gt;inject custom javascript&lt;/a&gt; into web&amp;nbsp;pages.&lt;/li&gt;
&lt;li&gt;It's possible to &lt;a href="https://code.google.com/chrome/extensions/messaging.html"&gt;pass messages&lt;/a&gt; between multiple pages running in
Chrome. This is an implementation of &lt;a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html"&gt;HTML5 postMessage&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With these two tools, we can inject javascript that implements a message
listener into a target page. This lets you define a JavaScript API
around the target page. In my case, the API around thesixtyone.com was
the following set of simple commands: next, previous, play, pause and
getSongInfo. Once we have injected JavaScript running in
thesixtyone.com, however, it's impossible to call the site's native
JavaScript for security reasons 
(from &lt;a href="http://code.google.com/chrome/extensions/content_scripts.html"&gt;Chrome Extension Developer Guide&lt;/a&gt;):&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Content scripts execute in a special environment called an isolated
  world. They have access to the DOM of the page they are injected into,
  but not to any JavaScript variables or functions created by the&amp;nbsp;page.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thus, I had to resort to some unfortunate hackery: generating fake mouse
clicks. One of the benefits of Chrome Extension writing is that there's
no cross-browser issues to deal with, which makes this sort of trick
much more&amp;nbsp;reliable:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function simulateClick(elementId) {
  var clickEvent = document.createEvent('MouseEvents');
  clickEvent.initMouseEvent('click', true, false,  document,
      0, 0, 0, 0, 0, false, false, false, false, 0, null);
  document.getElementById(elementId).dispatchEvent(clickEvent);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Overall, the interaction between the popup, background and injected code
is rather complex and looks something like&amp;nbsp;this: &lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2010/chrome-extension-diagram.png" alt="image" /&gt;&lt;/p&gt;

&lt;p&gt;One Chrome issue came up in the course of development: when you change
an iframe's src attribute such that the only difference compared to the
old one is the hash part of the URL, the iframe src page does not
refresh. I haven't had the chance to test test this on other browsers&amp;nbsp;yet.&lt;/p&gt;

&lt;p&gt;Strictly speaking, what I described isn't really a mashup, since I only
use one source. However, this approach is scalable to multiple sources,
and could easily be used to mash multiple web applications up inside an
extension. I hope this post is useful for people trying to wrap one or
many cross-domain websites in their&amp;nbsp;extension. &lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2010/radio-61-screenshot.png" alt="image" /&gt; &lt;/p&gt;

&lt;p&gt;I added a sprinkle of design and out came a Chrome Extension called
Radio 61! Let me know what you think if you try it out. Source code is
available on &lt;a href="https://github.com/borismus/Radio-61"&gt;my github&lt;/a&gt;.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/chrome-extension-mashups</feedburner:origLink></entry><entry><title>The Best Question Answering Sites</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/0wjsHevu4O8/best-question-answering-sites" /><updated>2010-10-25T00:00:36Z</updated><published>2010-10-25T00:00:36Z</published><id>http://smus.com/best-question-answering-sites</id><content type="html">
      
&lt;h1&gt;
  The Best Question Answering&amp;nbsp;Sites
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Oct. 25, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Oct. 25, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;There are hundreds of question answering sites on the Internet. Most of
them focus on specific topics (&lt;a href="http://stackoverflow.com"&gt;Stack Overflow&lt;/a&gt; for software
engineering, &lt;a href="http://news.ycombinator.com"&gt;Hacker News&lt;/a&gt; for startups, etc). However, a number of
Q&amp;amp;A sites exist for answering any kind of question, including the
contentious &lt;a href="http://www.somethingawful.com/flash/shmorky/babby.swf"&gt;"How is Babby formed?"&lt;/a&gt;. Open domain Q&amp;amp;A sites have a
distinct advantage over closed domain sites, since there is no need to
know which closed domain site is most appropriate for the question at
hand. Still, the asker needs to decide which open domain Q&amp;amp;A site to ask
on! What follows is a self-referential approach to ranking popular open
domain Q&amp;amp;A&amp;nbsp;sites.&lt;/p&gt;

&lt;h2&gt;Who questions the Q&amp;amp;A&amp;nbsp;sites?&lt;/h2&gt;

&lt;p&gt;I compiled a list of the top 7 most popular Q&amp;amp;A sites. Since
&lt;a href="http://alexa.com"&gt;alexa.com&lt;/a&gt; doesn't track subdomains (ex. &lt;a href="http://answers.yahoo.com"&gt;answers.yahoo.com&lt;/a&gt;, and
&lt;a href="http://askville.amazon.com"&gt;askville.amazon.com&lt;/a&gt;), I was forced to use &lt;a href="http://compete.com"&gt;compete.com&lt;/a&gt;. I then
asked each popular site a "recursive"&amp;nbsp;question:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What is the best question answering site (besides $SITENAME), and&amp;nbsp;why?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Eventually, most Q&amp;amp;A sites responded with one or two answers, which I
harvested and analyzed. Clearly this sample is too small to draw any
scientifically valid conclusions, so my analysis is anecdotal at best.
Still it paints an interesting picture of the open domain Q&amp;amp;A space. In
addition to the responses themselves, I looked at other data, such as
response time and response length. The following diagram summarizes the&amp;nbsp;results. &lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2010/qa-analysis.png" alt="image" /&gt; &lt;/p&gt;

&lt;p&gt;The arrows in the above diagram represent
recommendations from one community to another. The surveyed Q&amp;amp;A sites
are grouped based on popularity into three buckets. The most popular
bucket includes Yahoo! Answers and Answers.com, with 44 million and 31
million monthly uniques respectively. Despite having been around for the
longest (Answers.com launched in 1996), these are the most useless Q&amp;amp;A
websites. It took 4 days to get the answer "Another good answering site
is answers.com" from a Y! Answers member, and no response ever came from
Answers.com in 3&amp;nbsp;weeks.&lt;/p&gt;

&lt;p&gt;The moderately popular bucket of Q&amp;amp;A sites
includes Answerbag, Mahalo Answers and AskVille. These sites get about 4
million uniques a month, and generally gave reasonable answers. Mahalo
answers yielded the longest response, a 120 word paragraph recommending
Aardvark. The least visited sites and relative newcomers to the scene
are the most promising and active communities. Aardvark's response speed
is unparalleled, the first answer taking just 17 minutes, probably due
to GTalk integration. While Quora didn't actually yield a Q&amp;amp;A site
recommendation, the respondent produced an reasonable retort to my
rather questionable&amp;nbsp;question.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A Q&amp;amp;A "uber-site" that will do a good job being all things to all
  people is simply not going to happen - it is like "one size fits all"
  underwear, which never really fits&amp;nbsp;anyone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;The&amp;nbsp;Ranking&lt;/h2&gt;

&lt;p&gt;Overall, here's my anecdotal list of top 7 open domain Q&amp;amp;A sites (and
links to the questions I&amp;nbsp;posted):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="http://vark.com/t/Tljh9Q"&gt;Aardvark&lt;/a&gt; – Quick answers to your questions. Welcome to&amp;nbsp;Aardvark!&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.quora.com/What-is-the-best-question-answering-site-besides-Quora-and-why"&gt;Quora&lt;/a&gt; - Your question is flawed. Allow me to explain&amp;nbsp;why.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.mahalo.com/answers/what-is-the-best-question-answering-site-besides-mahalo-and-why"&gt;Mahalo&lt;/a&gt; – I'll answer your question with quite a bit of detail,
but it'll take a&amp;nbsp;while.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.answerbag.com/a_view/9737054"&gt;Answerbag&lt;/a&gt; - heres a decent answer but forget&amp;nbsp;punctuation&lt;/li&gt;
&lt;li&gt;&lt;a href="http://askville.amazon.com/question-answering-site-askville/AnswerViewer.do?requestId=74225856&amp;amp;startIndex=1&amp;amp;filter=ActivityOnQuestionsOnly"&gt;AskVille&lt;/a&gt; - Takes forever to give you a really bizarre&amp;nbsp;answer.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://answers.yahoo.com/question/index;_ylt=AlzEGEI5OLG69C1Ev0RgR4Pty6IX;_ylv=3?qid=20101007131829AA8cjLR"&gt;Yahoo! Answers&lt;/a&gt; – This community is brain damaged. Don't&amp;nbsp;bother.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://wiki.answers.com/Q/What_is_the_best_question_answering_site_besides_Answers_dot_com_and_why"&gt;Answers.com&lt;/a&gt; – This community is inactive. Don't&amp;nbsp;bother.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With a lot more answers from the Q&amp;amp;A communities, and some notion of
weight, you could imagine doing a PageRank-style calculation to
determine a more meaningful ordering of the top Q&amp;amp;A sites. We leave the
details of that approach to the reader :) PS. Thanks Sean, Jenn and&amp;nbsp;Pat!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/best-question-answering-sites</feedburner:origLink></entry><entry><title>An Onslaught of Mobile HTML Games</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/_vvuCylVvYk/mobile-html-games" /><updated>2010-10-16T10:55:06Z</updated><published>2010-10-16T10:55:06Z</published><id>http://smus.com/mobile-html-games</id><content type="html">
      
&lt;h1&gt;
  An Onslaught of Mobile HTML&amp;nbsp;Games
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Oct. 16, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Oct. 16, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;HTML5 games are really picking up. Casual Girl Gamer recently produced a
nice &lt;a href="http://www.casualgirlgamer.com/articles/entry/28/The-Best-30-HTML-5-games/"&gt;list of impressive titles&lt;/a&gt;. The modern web platform (namely,
fast javascript and canvas) is incredibly promising to a game developer.
The advantages that it brings are huge: no installation required, and
ubiquitous cross platform compatibility. I took a practical look at
games in the HTML5 mobile space, taking &lt;a href="http://lostdecadegamesapp.appspot.com/"&gt;Onslaught!&lt;/a&gt;, a particularly
fun and well written game, and &lt;a href="/x/onslaught/"&gt;porting it to Android/iPhone&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Perhaps porting is too strong a word here. Onslaught! runs and performs
reasonably well on my Nexus One running Android 2.2.1. The only problem
is that the game uses keyboard input, making it completely unplayable on
mobile devices. Luckily, the controls are quite simple, and only require
a directional pad and two buttons. So I decided to build an on-screen
virtual game controller, not unlike those found in many native iPhone
games.  At first I was inclined to build the controls by extending the
game itself (using the canvas element), but then decided that an
HTML-based approach is better (since it saves the trouble of hit
detection), and might even work as a generic controller for other&amp;nbsp;games.&lt;/p&gt;

&lt;p&gt;Hoping to create a general game controller for Android/iPhone, I sought
to generate keyboard events from JavaScript, based on touch input. While
this is possible if your key event handlers are written in a particular
framework (&lt;a href="http://api.jquery.com/keydown/"&gt;say jQuery&lt;/a&gt;), it seems to be generally impossible 
&lt;a href="http://stackoverflow.com/questions/1601593/fire-tab-keypress-event-in-javascript"&gt;for security reasons&lt;/a&gt;. I wrote an Onslaught!-specific &lt;a href="/x/onslaught/js/controller.js"&gt;controller&lt;/a&gt;
that should be easy to port to any other game. The controller simply
embeds the Onslaught! game in it's original 640x480 resolution, which is
then scaled to the device size using the &lt;a href="http://www.quirksmode.org/mobile/viewports.html"&gt;meta viewport&lt;/a&gt; element. The
result is a mobile game that's playable on Nexus One, and
not-quite-playable on iPod Touch (2nd gen) due to slower JavaScript
execution. These are the only two devices I currently have access to, so
I would appreciate it if you could [try it][porting it to
Android/iPhone] on your Android or iOS device (in landscape mode) and
let me know how it&amp;nbsp;goes!&lt;/p&gt;

&lt;p&gt;&lt;a href="/x/onslaught/"&gt;&lt;img src="/media/images/blog/2010/onslaught.png" alt="image" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;I ran into a few stumbling blocks as I was developing and testing this
port, most of which involve the Android browser on Nexus One running
Android&amp;nbsp;2.2.1.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Very immature touch event (ontouchstart, ontouchend, etc) support.
In fact, the &lt;strong&gt;browser doesn't seem to support multi-touch at all&lt;/strong&gt;
(ie. only one touch can be registered at a time). In contrast,
Safari for iOS supports multitouch events quite well. For complete
details, see this &lt;a href="http://quirksmode.org/mobile/tableTouch.html"&gt;quirksmode writeup&lt;/a&gt; and this &lt;a href="http://code.google.com/p/android/issues/detail?id=11909"&gt;bug report&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The Android browser completely ignores many properties in the meta
viewport element's content attribute. Specifically, the &lt;strong&gt;browser
doesn't react to initial-scale, minimum-scale, maximum-scale and
width&lt;/strong&gt;. As a result, I had to hack around this issue by abusing an
Android-only property called &lt;a href="http://developer.android.com/reference/android/webkit/WebView.html"&gt;target-densityDpi&lt;/a&gt;. I suspect that I
may be doing something wrong here, since it's a pretty fundamental
issue. Still I logged a &lt;a href="http://code.google.com/p/android/issues/detail?id=11912"&gt;bug&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Less significant but still noteworthy, the CSS &lt;strong&gt;:active selector
does not activate&lt;/strong&gt; on the Android browser (at least for div
elements). The reference implementation is iOS, where a touchstart
event on a div element causes it to become :active until a touchend&amp;nbsp;event.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the short term, HTML as a gaming platform is emerging as a real Flash
killer. In the long term, I wouldn't be surprised if HTML games will be
widely played on Mac, PC, TV console, and mobile phone platforms.
Whatever happens, browsers will continue to be pushed to conform to
modern specifications and perform ever better, making mobile rich web
applications more and more&amp;nbsp;feasible. &lt;/p&gt;

&lt;p&gt;In closing, many thanks to &lt;a href="http://blog.lostdecadegames.com/"&gt;Lost Decade Games&lt;/a&gt; team for writing such a
sweet game and not obfuscating the JavaScript! Oh, and a reminder that
if you're working on a HTML game, be sure to submit it to 
&lt;a href="http://mozillalabs.com/gaming/2010/09/30/game-on-2010-is-here/"&gt;Mozilla's Game On&lt;/a&gt; contest, then add some touch controls and submit
it to this &lt;a href="http://www.html5contest.com/"&gt;mobile HTML game contest&lt;/a&gt;. Let the games&amp;nbsp;begin!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/mobile-html-games</feedburner:origLink></entry><entry><title>Instant Search in 60 Lines</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/qAOtgbvHBpE/instant-search" /><updated>2010-10-09T02:00:04Z</updated><published>2010-10-09T02:00:04Z</published><id>http://smus.com/instant-search</id><content type="html">
      
&lt;h1&gt;
  Instant Search in 60&amp;nbsp;Lines
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Oct. 9, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Oct. 9, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Google recently unveiled Instant, a search enhancement which show
results as you type. The real technical challenge here is scaling the
backend, which now needs to handle a lot more load. The frontend
implementation, however, is quite simple. Yearning for some web
development, I decided to get my hands dirty. Here's a minimal
&lt;a href="/x/instant-search.html"&gt;implementation&lt;/a&gt; in under 60 lines of jQuery&amp;nbsp;code.&lt;/p&gt;

&lt;p&gt;Instant Search relies on two separate data sources: a suggestions API,
and a web search API. Roughly speaking, it works as follows: as you type
in the search box, AJAX requests are made to the suggestion API. The top
suggestion is then used as the query to search. In this case, I used the
following two services, both of which support&amp;nbsp;JSONP:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Suggest: &lt;a href="http://suggestqueries.google.com/complete/search"&gt;http://suggestqueries.google.com/complete/search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Search: &lt;a href="http://ajax.googleapis.com/ajax/services/search/web"&gt;http://ajax.googleapis.com/ajax/services/search/web&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To make your own search instant, all you need are your own suggest and
search feeds, and some tweaks to my code. Just make sure your server can
handle the&amp;nbsp;load!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/instant-search</feedburner:origLink></entry><entry><title>Stairs or Elevator? Use Bluetooth!</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/AXURp4g2dHE/stairs-elevator-bluetooth" /><updated>2010-09-30T12:17:31Z</updated><published>2010-09-30T12:17:31Z</published><id>http://smus.com/stairs-elevator-bluetooth</id><content type="html">
      
&lt;h1&gt;
  Stairs or Elevator? Use&amp;nbsp;Bluetooth!
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Sept. 30, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Sept. 30, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Most people carry mobile phones, and many of those phones have
integrated bluetooth functionality. Some of these phones are in
discoverable mode, making them detectable by other bluetooth devices
(Note: many new phones disable discoverable mode on a timer). By
building and deploying such scanners in a bluetooth sensor network, we
can collect a lot of interesting information about people's behavior in
public places. This post is about a minimal compelling application of
this&amp;nbsp;technology.&lt;/p&gt;

&lt;p&gt;Unfortunately we had just 3 makeshift internet-enabled bluetooth sensor
nodes at our disposal. Under this constraint, we brainstormed several
interesting scenarios where a bluetooth sensor network can be used to
collect data, for&amp;nbsp;example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;People's movement patterns in a bar&amp;nbsp;district&lt;/li&gt;
&lt;li&gt;Tourists following informational signs to points of&amp;nbsp;interest&lt;/li&gt;
&lt;li&gt;Passengers riding a public bus&amp;nbsp;system&lt;/li&gt;
&lt;li&gt;Travel between floors in a&amp;nbsp;building&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We decided to study patterns of travel (ie. climbing stairs vs. taking
the elevator) between two floors of a university building. We installed
three sensors: two on separate floors, and the third on the stairwell
connecting them (blue circles represent bluetooth scanner nodes in the
bluetooth sensor&amp;nbsp;network).&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2010/problem.png" alt="image" /&gt; &lt;/p&gt;

&lt;p&gt;This is a specific example of a more general point-to-point problem with
two possible paths. From this simple setup, It's possible to compute
whenever a trip was made from floor A to floor B, and whether or not the
traveler took the stairs or the elevator. Suppose that there was a trip
from A to B. If the trip was also made through C, it must have been made
via stairs, otherwise, it must have been made via elevator. Based on
this basic data, we can infer a number of features, some of which&amp;nbsp;are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Popularity of each route (ex. stairs more popular in the&amp;nbsp;morning)&lt;/li&gt;
&lt;li&gt;Habit (ex. people that take the elevator always take the&amp;nbsp;elevator)&lt;/li&gt;
&lt;li&gt;Temporal patterns (ex. some people consistently take the elevator in
the&amp;nbsp;morning)&lt;/li&gt;
&lt;li&gt;Duration of trips (ex. stairs down is faster, elevator up is&amp;nbsp;faster)&lt;/li&gt;
&lt;li&gt;Waiting time at each terminal (ex. waiting times higher for&amp;nbsp;elevator)&lt;/li&gt;
&lt;li&gt;Direction preferences (ex. many people prefer to take elevator up,
stairs&amp;nbsp;down)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unfortunately this project finished before any data was actually
collected! Luckily, the implementation is ready for use if you want to
take over! The following describes a simple implementation using EeePCs
or&amp;nbsp;MacMinis:&lt;/p&gt;

&lt;h2&gt;Implementing the Sensor&amp;nbsp;Network&lt;/h2&gt;

&lt;p&gt;The sensor network necessary for the inter-floor experiment consists of
several&amp;nbsp;components:&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2010/architecture.png" alt="image" /&gt; &lt;/p&gt;

&lt;p&gt;Each scanner is a computer with a bluetooth modem and a wireless
connection. We used two Mac Minis running OS X and one ASUS EeePC
running Linux. Every computer executed a &lt;a href="/x/bluetooth-network/bluetooth_scanner.py"&gt;bluetooth scanner program&lt;/a&gt;,
which scanned for bluetooth devices in the vicinity every 10 seconds,
and updated its list of nearby device IDs. Whenever the list of nearby
devices changed, it reported an ENTER or EXIT notification to the
central server via an HTTP&amp;nbsp;POST. &lt;/p&gt;

&lt;p&gt;The central server runs a MySQL database to store all notifications from
the scanners. Periodically, this &lt;a href="/x/bluetooth-network/trip_analyzer.py"&gt;analyzer program&lt;/a&gt; crawls the
database to extract trips from the data (ie. a device EXITs at one
sensor and subsequently ENTERs at another one). It also discerns between
trips taken by stairs and by elevator. Once trips are extracted, they
are appended to a Google Spreadsheet. This happens in "delayed
realtime", since a trip can only be inferred once the start and endpoint
are&amp;nbsp;known.&lt;/p&gt;

&lt;p&gt;The final component is a custom visualization written using &lt;a href="http://raphaeljs.com/"&gt;Raphael&lt;/a&gt;
and the &lt;a href="http://code.google.com/apis/charttools/index.html"&gt;Google Visualization&lt;/a&gt; framework. This JavaScript program
polls the spreadsheet every 10 seconds to see if any new trips were
added. If new trips were found, the visualization would update itself,
representing each trip as a circle moving from one terminal to another
along the stairs or elevator path. In addition, this visualization
stores a frequency chart over time to show how popular each of the
possible paths&amp;nbsp;are.&lt;/p&gt;

&lt;p&gt;For more information on related projects, check out &lt;a href="http://dme.uma.pt/people/faculty/vassilis.kostakos/"&gt;Vassilis' work&lt;/a&gt;
at&amp;nbsp;UMa.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/stairs-elevator-bluetooth</feedburner:origLink></entry><entry><title>Skip to Skip: a Running Gesture</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/xPOep2AZaQc/skip-running-gesture" /><updated>2010-08-06T00:03:47Z</updated><published>2010-08-06T00:03:47Z</published><id>http://smus.com/skip-running-gesture</id><content type="html">
      
&lt;h1&gt;
  Skip to Skip: a Running&amp;nbsp;Gesture
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Aug. 6, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Aug. 6, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Like many other runners, I like to listen to my music while training.
Even with a playlist of running music, I often want to change the
currently playing track. There are currently two popular options of
doing this: using the device itself, or using a headphone remote. My
ubiquitous computing project from last Spring explores a third option:
imagine if your shoes had built-in accelerometers that allowed you to
skip mid-stride to change&amp;nbsp;tracks. &lt;/p&gt;

&lt;p&gt;The most obvious way to change tracks is directly through the music
player, but operating a touch screen while running is pretty annoying. A
much better way of changing tracks is by using a button attached to a
pair of headphones, but finding it, and then double clicking it is often
a frustrating experience. Some clicks don't get registered, so one often
ends up triple-clicking, which skips &lt;em&gt;back&lt;/em&gt; a track. My project explores
a novel way of changing tracks: mid-stride skip. This gesture is
detected by having an accelerometer in each shoe which tracks your
running patterns and detects when you perform the skip gesture. In
addition to being useful, this running gesture makes an otherwise
monotonous activity more varied and&amp;nbsp;enjoyable.&lt;/p&gt;

&lt;p&gt;I prototyped the skip-to-skip system with a wii remote attached to the
runner's lower leg, as pictured above. A computer is paired to the
wiimote, collecting accelerometer data (especially in the axis
corresponding to the runner's vertical movement). This communication is
established using a wii library from a &lt;a href="/prototyping-wii-remote-python/"&gt;previous post&lt;/a&gt;. The naive
algorithm I use for detecting skips works as&amp;nbsp;follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find peaks by looking at the 1st derivative (positive slope,
negative slope&amp;nbsp;pairs)&lt;/li&gt;
&lt;li&gt;Discard insignificant peak values (under a&amp;nbsp;threshold)&lt;/li&gt;
&lt;li&gt;Compute distances between&amp;nbsp;peaks&lt;/li&gt;
&lt;li&gt;Look at the last 5 distances, and compute the mode. That's the&amp;nbsp;pace.&lt;/li&gt;
&lt;li&gt;Look for declinations from the pace characteristic of a&amp;nbsp;skip.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once my gesture recognition code worked reasonably well, I ran a user
study to see how people liked this method compared to the touch screen
and headphone remote. After a few hitches (including getting kicked out
of the Madeira Tecnopolo), I managed to test the prototype with many of
my Madeiran classmates (thanks guys!). Most people preferred the
skip-to-skip method over both the direct smartphone and headphone remote
methods, which is&amp;nbsp;promising.&lt;/p&gt;

&lt;p&gt;For more details on this project, check out the &lt;a href="/media/images/blog/2010/running-gestures-paper.pdf"&gt;paper&lt;/a&gt; that
&lt;a href="http://dme.uma.pt/people/faculty/vassilis.kostakos/"&gt;Vassilis Kostakos&lt;/a&gt; and I will present at &lt;a href="http://www.ubicomp2010.org/"&gt;Ubicomp 2010&lt;/a&gt; in
Copenhagen. If you're interested in the source code, it's located on
&lt;a href="http://github.com/borismus/Running-Gestures"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; here's the &lt;a href="/media/images/blog/2010/running-gestures-poster.pdf"&gt;poster&lt;/a&gt;. The font-size is scary huge
on&amp;nbsp;A0!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/skip-running-gesture</feedburner:origLink></entry><entry><title>Minimal Business Card Design</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/aEdQldGkJe0/minimal-business-card-design" /><updated>2010-07-21T00:11:36Z</updated><published>2010-07-21T00:11:36Z</published><id>http://smus.com/minimal-business-card-design</id><content type="html">
      
&lt;h1&gt;
  Minimal Business Card&amp;nbsp;Design
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="July 21, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;July 21, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Business card design is a tricky art. It's a fairly constrained space,
but that's what design is all about, right? I'm ordering a personal set
of &lt;a href="http://us.moo.com/en/products/minicards.php"&gt;moo mini cards&lt;/a&gt;. These are small, two sided prints. One side
contains an image, and the other contains contact information. On the
image side, I'm putting snippets of travel photography. The other side
is by default a conventional list of contact information, but moo
conveniently allows it to be replaced by a custom&amp;nbsp;image. &lt;/p&gt;

&lt;p&gt;Rather than having each field separately labeled, I tried to uncover as
much information as possible within my email address. Hidden inside are
my first name, last name, website, and twitter account! Here's a minimal
design concept that tries to break it&amp;nbsp;down. &lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2010/borismus_business_card.png" alt="image" /&gt; &lt;/p&gt;

&lt;p&gt;In my case, my email address contains all relevant info except the phone
number which I don't want to include anyway. I hope that this simple
idea can inspire you to come up with something more polished. Looking
forward to what you come up with! P.S. Thanks for the help, &lt;a href="http://www.jennlu.com/"&gt;Jenn&lt;/a&gt;!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/minimal-business-card-design</feedburner:origLink></entry><entry><title>Android-powered Mindstorms</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/KhSQDcD6-40/android-powered-mindstorms" /><updated>2010-06-27T23:27:53Z</updated><published>2010-06-27T23:27:53Z</published><id>http://smus.com/android-powered-mindstorms</id><content type="html">
      
&lt;h1&gt;
  Android-powered&amp;nbsp;Mindstorms
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="June 27, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;June 27, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;A few projects around the internet use an Android phone to control the
LEGO Mindstorms NXT brick. Most involve an ugly hack in which the phone
communicates with a computer over WiFi, and the computer (paired to the
NXT through bluetooth) submits the command to the brick. These projects
typically use Android as a remote control for the NXT robot, and not as
part of the robot itself. Here is a missed opportunity: the NXT could be
augmented by an &lt;a href="http://developer.android.com/reference/android/hardware/Sensor.html"&gt;impressive list&lt;/a&gt; of sensors, GPS and Internet access
provided by an Android&amp;nbsp;phone. &lt;/p&gt;

&lt;p&gt;This project does just that, while eliminating the need for a computer in
the loop, so that the Android directly communicates to the NXT. This allows
for more powerful Android-powered NXT robots. As an example, I made a fully
autonomous twitter-controlled robot. The NXT uses two motors to spin in
place or move forward, and a third motor to control the tilt of a Android
phone cradle. The Android phone keeps track of its orientation (compass
heading and tilt), polls &lt;a href="http://search.twitter.com/"&gt;twitter search&lt;/a&gt; for new commands and sends
commands to the NXT brick. After each command completes, the Android phone
takes a picture and sends it to twitter. Any twitter user can look at the
last few photos, decide which command makes sense to perform next, and issue
it. This approach can be summarized succinctly as "&lt;a href="http://en.wikipedia.org/wiki/Crowdsourcing"&gt;crowdsourced&lt;/a&gt;
&lt;a href="http://en.wikipedia.org/wiki/Teleoperation"&gt;teleoperation&lt;/a&gt;". &lt;/p&gt;

&lt;p&gt;Here's a demonstration video of the robot in&amp;nbsp;action:&lt;/p&gt;

&lt;iframe title="YouTube video player" width="600" height="368"
  src="http://www.youtube.com/embed/ATQ_0tySttM" frameborder="0"
  allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;I think that this marriage of Android and NXT can fuel a very interesting
set of robots impossible to build with the NXT alone. &lt;a href="http://twitter.com/mindstorms"&gt;@mindstorms&lt;/a&gt; is
offline for now to save some battery life, but code is available at my new
&lt;a href="http://github.com/borismus/android-nxt"&gt;github&lt;/a&gt;. If you make use of my code or have some feedback, please reply&amp;nbsp;below!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/android-powered-mindstorms</feedburner:origLink></entry><entry><title>Prototyping with Wii Remotes in Python</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/KyqwOT5k1aE/prototyping-wii-remote-python" /><updated>2010-05-28T12:00:40Z</updated><published>2010-05-28T12:00:40Z</published><id>http://smus.com/prototyping-wii-remote-python</id><content type="html">
      
&lt;h1&gt;
  Prototyping with Wii Remotes in&amp;nbsp;Python
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="May 28, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;May 28, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;I've been working on a couple of researchy projects involving gait
recognition and running foot strike analysis. For my proof of concept, I
turned to the wiimote, everyone's favorite physical interaction
prototyping input device. Wiimotes are portable and rugged, and thus
well suited to high-intensity activities like running. They attach
easily to legs with a physio band, although the elastic tension tends to
cut off circulation. No big deal, though... it's For&amp;nbsp;Science! &lt;/p&gt;

&lt;p&gt;This article is not about attaching Wiis to legs (more on that at a
later date!), but about communicating with the Wii remote using python.
I started out by writing a Cocoa application to harvest accelerometer
data using the WiiRemote framework provided by the &lt;a href="http://darwiin-remote.sourceforge.net/"&gt;DarwiinRemote&lt;/a&gt;
project.  After some objective-c iterations of my initial gait
recognizer algorithm, I decided to port to python, an environment better
suited for light prototyping. There's a few packages explicitly
developed to integrate with the wiimote. &lt;a href="http://stackoverflow.com/questions/481943/python-with-wiimote-using-pywiiuse-module"&gt;Pywiiuse&lt;/a&gt; provides a
lightweight wrapper around the &lt;a href="http://www.wiiuse.net/"&gt;wiiuse&lt;/a&gt; library and does not work on
OS X. An alternative, &lt;a href="http://code.google.com/p/pywiimote/"&gt;pywiimote&lt;/a&gt; claims to be multiplatform but
pointedly isn't. Here's the start of their&amp;nbsp;code: &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from ctypes import *
kernel = windll.kernel32
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Having found no existing wiimote-specific python libraries that would
work on my platform, I had no choice but to dig a little into the
bluetooth-based protocol that the wiimote uses. I found all the details
in all their gory glory on the &lt;a href="http://wiibrew.org/wiki/Wiimote#Accelerometer"&gt;wiibrew wiki&lt;/a&gt;. The communication
protocol involves two open L2CAP sockets between the host and wiimote:
one for reading and one for writing. After an initialization string is
sent over the write socket, the wiimote springs into life and sends a
stream of data on the read socket. In this data are accelerometer values
and button presses. Here's a simple python snippet using the
&lt;a href="http://lightblue.sourceforge.net/"&gt;lightblue&lt;/a&gt;&amp;nbsp;library:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import sys, lightblue, hexbyte

WIIMOTE_DEVICE_NAME = 'Nintendo RVL-CNT-01'

# auto-discover nearby bluetooth devices
devs = lightblue.finddevices(getnames=True, length=5)
# find the one with the correct name
wiimote = [d for d in devs if d[1] == WIIMOTE_DEVICE_NAME] and d[0] or None
if not wiimote:
    print "No wiimotes found!"
    sys.exit(1)

# create a socket for writing control data
write_socket = lightblue.socket(lightblue.L2CAP)
write_socket.connect((wiimote, 0x11))

# create a socket for reading accelerometer data
read_socket = lightblue.socket(lightblue.L2CAP)
read_socket.connect((wiimote, 0x13))

# initialize the socket to the right mode
write_socket.send(hexbyte.HexToByte('52 12 00 33'))

# start reading data from it
while 1:
    byte = read_socket.recv(256 * 7)
    data = hexbyte.ByteToHex(byte)
    # do something interesting with the data
    print data
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You'll need &lt;a href="https://github.com/borismus/Running-Gestures/blob/master/hexbyte.py"&gt;hexbyte.py&lt;/a&gt; to run the above snippet. I hope
you (the wii remote wielding python fan) find this snippet useful. As a
side note, if you've figured how to pair a wiimote with an android phone
and released the code into the public domain, please let me know. Since
Android 2.2 still doesn't ship with L2CAP APIs, I hit the&amp;nbsp;wall.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/prototyping-wii-remote-python</feedburner:origLink></entry><entry><title>Nike+ Hacking with Python</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/7oFKqUHHAqc/nike-hacking-with-python" /><updated>2010-05-18T05:27:56Z</updated><published>2010-05-18T05:27:56Z</published><id>http://smus.com/nike-hacking-with-python</id><content type="html">
      
&lt;h1&gt;
  Nike+ Hacking with&amp;nbsp;Python
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="May 18, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;May 18, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="http://www.apple.com/ipod/nike/"&gt;Nike+&lt;/a&gt; is a clever little system designed by Apple and Nike to infer
the runner's speed and augment the running experience. The runner places
a small chip in his shoe which transmits data to the iPod using a
proprietary RF-based protocol. The chip contains a piezoelectric cell
which measures how long the foot exerted pressure on the ground.
According to &lt;a href="http://support.apple.com/kb/HT2293?viewlocale=en_US"&gt;Apple's FAQ&lt;/a&gt;, this contact time is directly related to
your pace. In this post I provide a snippet of python code for
collecting data through Sparkfun's&amp;nbsp;adapter. &lt;/p&gt;

&lt;p&gt;Sparkfun &lt;a href="http://www.sparkfun.com/commerce/tutorial_info.php?tutorials_id=41"&gt;dissected&lt;/a&gt; the transmitter and receiver and currently sell a
&lt;a href="http://www.sparkfun.com/commerce/product_info.php?products_id=8245"&gt;Nike+ Serial to USB adapter&lt;/a&gt;. There's been a number of &lt;a href="http://www.sparkfun.com/commerce/tutorial_info.php?tutorials_id=135"&gt;notable&lt;/a&gt;
&lt;a href="http://dub.washington.edu/pubs/46%0A"&gt;projects&lt;/a&gt; since, and quite a lot of interest in the system for
general&amp;nbsp;hackery.&lt;/p&gt;

&lt;p&gt;I wanted to
use Nike+ for my own project (a running bib that would automatically
display the runner's speed on the back). Unfortunately, the only
available implementations were in &lt;a href="http://www.sparkfun.com/datasheets/DevTools/iPod/Nike_iPod_Serial.zip"&gt;Visual Basic&lt;/a&gt; and &lt;a href="http://rtadlock.blogspot.com/2009/06/some-perl-code-for-nikeipod-serial.html"&gt;perl&lt;/a&gt;, neither
of which work on Mac. Here's a small script for Python on Mac OS X to
collect Nike+ data using SparkFun's&amp;nbsp;adapter. &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python
import serial
from hexbyte import *

def readbytes(number):
    buf = ''
    for i in range(number):
        byte = ser.read()
        buf += byte

    return buf

# open the appropriate serial port
ser = serial.Serial('/dev/tty.usbserial-A6007uDh', 57600, bytesize=serial.EIGHTBITS)

# send the following init string to the Nike+ device:
init1 = 'FF 55 04 09 07 00 25 C7'
ser.write(HexToByte(init1))

# listen for the response string: FF 55 04 09 00 00 07 EC
response1 = ByteToHex(readbytes(8))
assert response1 == 'FF 55 04 09 00 00 07 EC'

# send the second init string
init2 = 'FF 55 02 09 05 F0'
ser.write(HexToByte(init2))

# listen for the response string: FF 55 04 09 06 00 25 C8
response2 = ByteToHex(readbytes(8))
assert response2 == 'FF 55 04 09 06 00 25 C8'

# now we're ready to listen for actual data
print "nike+ initialized. listening for data"

while True:
    byte = ser.read()
    # if a byte is coming down the port,
    if byte:
        # get the rest of the message (34 chars)
        message = byte + readbytes(33)
        # and decipher it
        data = {
            'number': ByteToHex(message[11]),
            'uid': ByteToHex(message[7:11]),
            'data': ByteToHex(message[12:]),
        }
        print data
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You'll also need &lt;a href="https://github.com/borismus/Running-Gestures/blob/master/hexbyte.py"&gt;hexbyte.py&lt;/a&gt;, which contains convenience conversion
methods between binary and&amp;nbsp;hex.&lt;/p&gt;

&lt;p&gt;I'm still unable to fully make sense of this data.  Firstly, each step
seems to inexplicably generate 8 packets instead of one. Second, there
are 22 bytes in the Nike+ data &lt;a href="http://ipodlinux.org/wiki/Apple_Accessory_Protocol#Nike.2B_.28Mode_9.29"&gt;with an unknown structure&lt;/a&gt;, probably
containing pressure duration data. If someone figures out how to make
sense of this please let me&amp;nbsp;know!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/nike-hacking-with-python</feedburner:origLink></entry><entry><title>Jam Hunt: Friendly Jam Sessions</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/CS4mxv9feRo/jam-hunt" /><updated>2010-04-24T16:18:32Z</updated><published>2010-04-24T16:18:32Z</published><id>http://smus.com/jam-hunt</id><content type="html">
      
&lt;h1&gt;
  Jam Hunt: Friendly Jam&amp;nbsp;Sessions
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="April 24, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;April 24, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Ever wanted to join a band? I bet you have! Why? Because collaborative
music making is an incredibly enjoyable and rewarding experience. But
the barriers to entry are high: not only do you need to have baseline
musical skills, you also need considerable managerial talent to find and
bring together disorganized musicians. To find partners to jam with,
people use craigslist and band matching sites to try to establish
relationships with randoms. Why not leverage our social networks for
this&amp;nbsp;purpose?&lt;/p&gt;

&lt;p&gt;Okay, now that you're fully convinced that there's a huge opportunity to
tap into this friend-jam space, let me introduce &lt;a href="http://www.jamhunt.com/"&gt;Jam Hunt&lt;/a&gt;. The idea
behind Jam Hunt is to allow you to manage your musical profile by
specifying a list of instruments you are skilled at and a list of songs
you know how to play. If your friends also maintain such profiles, Jam
Hunt can look across the social graph and discover friends to try
jamming with. Thus, the application enables spontaneous &lt;em&gt;flash bands&lt;/em&gt;
(in the spirit of &lt;a href="http://en.wikipedia.org/wiki/Flash_mob"&gt;flash mobs&lt;/a&gt;) to form brief, friendly jam&amp;nbsp;sessions. &lt;/p&gt;

&lt;p&gt;I developed a Jam Hunt prototype for &lt;a href="http://www.hcii.cmu.edu/courses/software-architecture-user-interfaces-0"&gt;SAUI class&lt;/a&gt; while pleasantly
stranded in Pittsburgh as a result of &lt;a href="http://www.nytimes.com/2010/04/25/weekinreview/25kimmelman.html"&gt;Eyjafjallajökull's eruption&lt;/a&gt;.
The assignment stipulated that I implement a Facebook application, which
was initially distressing to me, due to the &lt;a href="http://www.codinghorror.com/blog/2007/06/avoiding-walled-gardens-on-the-internet.html"&gt;walled-garden nature&lt;/a&gt; of
the platform. I was slightly mollified when I discovered three&amp;nbsp;things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;that there is a &lt;a href="http://wiki.developers.facebook.com/index.php/User:PyFacebook_Tutorial"&gt;way to use django&lt;/a&gt; to develop Facebook&amp;nbsp;apps.&lt;/li&gt;
&lt;li&gt;that the &lt;em&gt;average&lt;/em&gt; Facebook user has a whopping 130&amp;nbsp;friends.&lt;/li&gt;
&lt;li&gt;that there is a potentially &lt;a href="http://developers.facebook.com/docs/opengraph"&gt;bright future&lt;/a&gt; ahead for&amp;nbsp;Facebook&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My prospects for having fun while developing something useful, and
potentially viral, and not entirely evil, were on the&amp;nbsp;rise. &lt;/p&gt;

&lt;p&gt;As it turns out, developing a django application for Facebook is no
cakewalk.  Firstly, python is not an officially supported language for
Facebook development. As a result, there are a number of
&lt;a href="http://code.google.com/p/simplefacebook/"&gt;semi-abandoned&lt;/a&gt; &lt;a href="http://github.com/sciyoshi/pyfacebook/tree/master"&gt;open source projects&lt;/a&gt; to &lt;a href="http://code.google.com/p/minifb/"&gt;bridge that gap&lt;/a&gt;.
Coupled with Facebook's outright disregard for API stability, calling
Notifications.send and Stream.write were next to impossible from python.
But surely writing PHP applications must be a breeze, right? Well,
during the week that I was developing Jam Hunt,
&lt;a href="http://forum.developers.facebook.com/"&gt;forum.developers.facebook.com&lt;/a&gt;, one of the most indexed resources on
Facebook API questions, was consistently down. The sorry state of their
hybrid &lt;a href="http://wiki.developers.facebook.com/index.php/New_Design_Platform_Changes"&gt;documentation-wiki&lt;/a&gt; system was just icing on the&amp;nbsp;cake. &lt;/p&gt;

&lt;p&gt;Anyway, enough bitching! If you have some spare cycles and a Facebook
account, please try &lt;a href="http://www.jamhunt.com/"&gt;Jam Hunt&lt;/a&gt;. Whether you find it interesting,
appealing, pointless, ugly, or just outright broken, let me&amp;nbsp;know.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/jam-hunt</feedburner:origLink></entry><entry><title>CHI 2010 Poster</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/9AtB24ZGyRc/chi-2010-poster" /><updated>2010-03-21T12:40:33Z</updated><published>2010-03-21T12:40:33Z</published><id>http://smus.com/chi-2010-poster</id><content type="html">
      
&lt;h1&gt;
  CHI 2010&amp;nbsp;Poster
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="March 21, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;March 21, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;I'm confirmed to go to &lt;a href="http://chi2010.org/"&gt;CHI2010&lt;/a&gt; in Atlanta, so I spent some time
making a poster for the &lt;a href="/ubiquitous-drums"&gt;Ubiquitous Drums&lt;/a&gt; project that was
miraculously accepted as a &lt;a href="/media/images/blog/2010/ubiquitous-drums-paper.pdf"&gt;WIP&lt;/a&gt;. It's nice to pretend to be a visual
designer sometimes. Thanks to Mark, Jenn and Rebeca for your&amp;nbsp;input.&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2010/ubiquitous-drums-poster.jpg" alt="image" /&gt;&lt;/p&gt;

&lt;p&gt;I would really appreciate additional suggestions on how to improve the
&lt;a href="/media/images/blog/2010/ubiquitous-drums-poster.pdf"&gt;poster&lt;/a&gt;, or your thoughts on academic posters in general. Why are
they usually so&amp;nbsp;ugly?&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/chi-2010-poster</feedburner:origLink></entry><entry><title>Lightweight Wordpress on Slicehost</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/hp4JQNX8EMg/lightweight-wordpress-on-slicehost" /><updated>2010-02-26T05:18:28Z</updated><published>2010-02-26T05:18:28Z</published><id>http://smus.com/lightweight-wordpress-on-slicehost</id><content type="html">
      
&lt;h1&gt;
  Lightweight Wordpress on&amp;nbsp;Slicehost
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Feb. 26, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Feb. 26, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;I recently switched from shared hosting to a VPS, expecting to get an
immediate and automatic performance boost. I was overly optimistic and
ran into memory trouble right away. After endlessly struggling with
Apache and mod_php configuration, I was ready to give up. Then on a
whim, I switched to nginx/fastcgi to see the average response time drop
from 1500ms to&amp;nbsp;300ms: &lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2010/performance.png" alt="image" /&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.webfaction.com"&gt;WebFaction&lt;/a&gt; is a great shared hosting environment, providing tons of
functionality and impeccable customer support. However, shared hosting
means that multiple users share resources on a single physical machine,
resulting in wildly fluctuating &lt;a href="http://www.pingdom.com"&gt;site performance and uptime&lt;/a&gt; at the
whim of other clients. Also, I prefer to do system administration from
the command line, but WebFaction provides a powerful but clunky
web-based administrative interface. All in all, despite my respect for
WebFaction, I said goodbye and switched to &lt;a href="http://www.slicehost.com"&gt;Slicehost&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;I ordered my shiny new slice and plunged into configuration. I migrated
wordpress databases and set up Apache with mod&lt;em&gt;php. Everything seemed
to work reasonably well until Apache ran for a few hours and began
consuming my memory allowance. The slice started thrashing and
performance fell to a crawl.  I found the culprit to be in my
mpm&lt;/em&gt;prefork_module MaxClients and MaxRequestsPerChild configuration,
but even after tweaking those, my slice was hitting the wall pretty
quickly. I nearly returned to cushy WebFaction, where I had marginal
performance without the headache, but decided to experiment&amp;nbsp;more. &lt;/p&gt;

&lt;p&gt;Nginx is a minimalist HTTP server written by &lt;a href="http://sysoev.ru/en/"&gt;Igor Sysoev&lt;/a&gt; for
&lt;a href="http://www.rambler.ru/"&gt;Rambler&lt;/a&gt;. It's now being used by &lt;a href="http://www.wordpress.com"&gt;Wordpress&lt;/a&gt; and other high profile
sites. Seeking help from the internet, I eventually came across Thomasz
Sterna's &lt;a href="http://tomasz.sterna.tv/2009/04/php-fastcgi-with-nginx-on-ubuntu/"&gt;php-fastcgi init script&lt;/a&gt; and adapted it for my slice,&amp;nbsp;using&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;PHP_FCGI_CHILDREN=5
PHP_FCGI_MAX_REQUESTS=100
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;for the low-memory environment. In addition, I installed the 
&lt;a href="http://wordpress.org/extend/plugins/wp-super-cache/"&gt;WP Super Cache&lt;/a&gt;&amp;nbsp;plugin.&lt;/p&gt;

&lt;p&gt;For my &lt;a href="http://www.guitarunleashed.com"&gt;django site&lt;/a&gt;, I wrote an init script for 
&lt;a href="/wp-content/uploads/2010/02/django-fastcgi.txt"&gt;launching the django fcgi&lt;/a&gt; server via django's manage.py. Here's the
important&amp;nbsp;part:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;MAXCHILDREN=5
MAXSPARE=5
MINSPARE=2 
# ... 
start-stop-daemon --quiet --start \
  --pidfile $PIDFILE --chuid "$USER" \
  --exec /usr/bin/env -- python $SITEPATH/$SITENAME/manage.py runfcgi \
  --settings=settings \
  host=$HOST port=$PORT pidfile=$PIDFILE \
  maxchildren=$MAXCHILDREN maxspare=$MAXSPARE minspare=$MINSPARE
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I'm happy with the performance both for django and php-powered sites.
However, I'm just guessing when it comes to values of &lt;code&gt;MAXCHILDREN&lt;/code&gt;,
&lt;code&gt;MAXSPARE&lt;/code&gt; and &lt;code&gt;MINSPARE&lt;/code&gt;, &lt;code&gt;PHP_FCGI_CHILDREN&lt;/code&gt; and
&lt;code&gt;PHP_FCGI_MAX_REQUESTS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Do you have insight on how to tweak these parameters? Is your VPS
hosting configuration&amp;nbsp;better?&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/lightweight-wordpress-on-slicehost</feedburner:origLink></entry><entry><title>Visualizing MTurk Requesters</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/XcYUQzM50Cw/visualizing-mturk-requester" /><updated>2010-02-06T12:39:41Z</updated><published>2010-02-06T12:39:41Z</published><id>http://smus.com/visualizing-mturk-requester</id><content type="html">
      
&lt;h1&gt;
  Visualizing MTurk&amp;nbsp;Requesters
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Feb. 6, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Feb. 6, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;I signed up to do one month of paid research at CMU|Portugal before
spring classes start. My task boils down to creating interesting
visualizations. The bad news is that I have no experience visualizing
data and the dataset I'm to visualize hasn't yet been collected.
Fortunately, I've always been &lt;em&gt;theoretically&lt;/em&gt; interested in data
visualization, so I was happy to have a solid excuse to explore the
subject. All I needed was a sufficiently rich data set, mad skills and a
bit of&amp;nbsp;inspiration.&lt;/p&gt;

&lt;p&gt;Lately, I've been pretty excited about squeezing some new potential out
of Mechanical Turk. Part of my research involves finding patterns in
Mechanical Turk requester strategies. A few weeks ago, I began gathering
data with a &lt;a href="/x/turk-visualizer/turkviz_scraper.py"&gt;python program&lt;/a&gt; that extracts all scrape-able information
about every HIT group and stores it in a sqlite3 database. This is quite
an interesting data set, so I pounced on the opportunity to visualize
it, killing two birds with one&amp;nbsp;stone.&lt;/p&gt;

&lt;p&gt;Due to lack of time, I decided to skip most of the 
&lt;a href="http://www.amazon.com/Visual-Display-Quantitative-Information/dp/096139210X"&gt;visualization literature&lt;/a&gt;. Instead, I found a great 
&lt;a href="http://www.visualcomplexity.com/vc/"&gt;visualization project database&lt;/a&gt; and started writing simple examples
using some popular
visualization languages and frameworks. I began with &lt;a href="http://processing.org/"&gt;Processing&lt;/a&gt; and
&lt;a href="http://processingjs.org/"&gt;Processing.js&lt;/a&gt; but quickly tired of the raster-based drawing model. I
turned to SVG with &lt;a href="http://raphaeljs.com/"&gt;Raphaël&lt;/a&gt; and jQuery and ended up building a simple
bubble chart. I had no specific visualization in mind, so my vague goal
was to be able to visualize data in &lt;a href="http://www.gapminder.org/"&gt;Hans Rosling&lt;/a&gt;'s favorite
five-dimensional (x, y, size, color, time) graph. Making this graph
animate, however, proved to be quite&amp;nbsp;difficult.&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2010/raphael.jpg" alt="image" /&gt; &lt;/p&gt;

&lt;p&gt;I turned to
the internet for help, and Itai Raz &lt;a href="http://www.youtube.com/watch?v=guhdYoPY3kM"&gt;explained to me&lt;/a&gt; in his thick
Israeli accent that the Google Visualization API is pretty sweet.
Moreover, it comes with the exact visualization I wanted to implement,
apparently called the &lt;a href="http://code.google.com/apis/visualization/documentation/gallery/motionchart.html"&gt;Motion Chart&lt;/a&gt;. Happily, Google Spreadsheets
export data in the format that the visualization framework consumes.
Thus my problem was greatly reduced to one of analyzing and uploading
data to a Google Spreadsheet. Turns out that this too is quite easy
using the python &lt;a href="http://code.google.com/p/gdata-python-client/"&gt;GData framework&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I decided to visualize differences between requester strategies on
Mechanical Turk. Every time I scrape, I generate a Average Reward (x),
Average Allotted Time (y), Total Number of Hits (size) graph for the top
50 requesters, and then upload it to a google spreadsheet. Here is the
&lt;a href="/x/turk-visualizer/turkviz_scraper.py"&gt;python program&lt;/a&gt; I wrote for this purpose. &lt;a href="http://www.borismus.com/wp-content/uploads/2010/02/turk_requester_visualizer.html"&gt;The results&lt;/a&gt; are
interesting and fun to play with. Google's Motion Chart visualization is
incredibly powerful and flexible. I won't go in detail into findings
from the data since it's irrelevant to this largely technical discussion
about visualization technologies. I'll soon get my hands on the data I'm
expecting and create a custom visualization for it with the Google
Visualization API. Stay&amp;nbsp;chooned!&lt;/p&gt;

&lt;p&gt;[]:&amp;nbsp;/x/turk-visualizer/&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/visualizing-mturk-requester</feedburner:origLink></entry><entry><title>Crowdsourcing Code</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/Q9V5hGOnu4c/crowdsourcing-code" /><updated>2010-01-16T14:46:01Z</updated><published>2010-01-16T14:46:01Z</published><id>http://smus.com/crowdsourcing-code</id><content type="html">
      
&lt;h1&gt;
  Crowdsourcing&amp;nbsp;Code
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Jan. 16, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Jan. 16, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;As a follow up to my &lt;a href="/crowdsourcing-articles-with-mechanical-turk/"&gt;last post&lt;/a&gt;, I posted a HIT on Mechanical Turk
asking 20 turkers if they know Java. I paid them 5 cents to answer the
question. Surprisingly, 9 of 20 claimed to know. I was amazed at how
strong selection bias was in this case, since surely not 50% of turkers
know how to program! I then asked those turkers who know Java to
complete the following trivial Java method. If they wrote it correctly,
I paid them a 45 cent&amp;nbsp;bonus.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public static String reverse(String source) {
  // your code here 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here are the&amp;nbsp;results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4 turkers used &lt;code&gt;StringBuffer.reverse&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;3 turkers created a new string by iterating through the original
string in&amp;nbsp;reverse&lt;/li&gt;
&lt;li&gt;1 used&amp;nbsp;recursion&lt;/li&gt;
&lt;li&gt;1 used &lt;code&gt;Collections.sort(l)&lt;/code&gt;. I'm not sure what was&amp;nbsp;intended&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was hoping that people would fill in the empty reverse method with
their code, but many of them implemented their own methods and helpers.
One person implemented their own class with extensive comments. This
data as a nice existence proof, indicating that turkers can be harnessed
for programming-related&amp;nbsp;crowdsourcing. &lt;/p&gt;

&lt;p&gt;I'd like to turn Mechanical Turkers into Mechanical Coders. Given a set
of unit tests and a method to implement, their work could be
automatically verified based on passing the unit tests. Furthermore,
some turkers could be tasked to write additional unit tests for this
method. Through this technique, it's conceivable to harness the power of
The Turk to implement whole classes. Code quality aside, what sort of
software quality could be achieved by following this&amp;nbsp;approach?&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/crowdsourcing-code</feedburner:origLink></entry><entry><title>Crowdsourcing Articles with Mechanical Turk</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/PQ2p3QhJXdA/crowdsourcing-articles-with-mechanical-turk" /><updated>2010-01-14T07:07:08Z</updated><published>2010-01-14T07:07:08Z</published><id>http://smus.com/crowdsourcing-articles-with-mechanical-turk</id><content type="html">
      
&lt;h1&gt;
  Crowdsourcing Articles with Mechanical&amp;nbsp;Turk
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Jan. 14, 2010"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Jan. 14, 2010&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Last semester at CMU, I was involved in a research project involving
&lt;a href="http://www.mturk.com/"&gt;Mechanical Turk&lt;/a&gt;. The goal was to get Mechanical Turk users (turkers)
to collaborate on creating online wikipedia-style articles. Prior to my
team's involvement, an undergraduate created a mediawiki-based platform
to allow turkers to collaborate on articles. Despite a high
compensation, few turkers completed the task. My team tackled the
problem and came up with some interesting videos on the&amp;nbsp;way. &lt;/p&gt;

&lt;p&gt;We began by conducting contextual interviews with turkers living in
Pittsburgh, all of whom rather unexpectedly, were female. The general
takeaway was clear: turkers are used to very short and repetitive tasks,
but article creation requires a prolonged period of concentration. Our
solution was to significantly tweak the task, making it seem less
arduous. In addition to simplifying the HIT's flow, we switched from
mediawiki to &lt;a href="http://www.etherpad.com/"&gt;etherpad&lt;/a&gt; as the article editing and collaboration
platform. As a result of these changes, we were able to churn out
turker-created articles on a given topic for under ten dollars. Here's a
video of turkers collaborating on an article about&amp;nbsp;Halloween:&lt;/p&gt;

&lt;iframe title="YouTube video player" width="600" height="480"
  src="http://www.youtube.com/embed/AmUq_Uovqek" frameborder="0"
  allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;We started out by creating an etherpad instance with a simple paragraph
about the topic, as well as some article quality guidelines. Next, we
created a series of Mechanical Turk HITs referencing the etherpad
instance's URL. We paid our turkers a quarter up front for accepting the
task, and provided a nickel (up to one dollar) every time they returned
to edit the pad. We had no good way to verify the bonus mechanism, so we
generally gave out the maximum bonus to every active collaborator.
Here's the evolution of an article on Windows&amp;nbsp;7: &lt;/p&gt;

&lt;iframe title="YouTube video player" width="600" height="480"
  src="http://www.youtube.com/embed/C7pV9fXIo0M" frameborder="0"
  allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;Watching the etherpad explode in color as multiple turkers
simultaneously edit the same article is still mesmerizing. Though the
quality of the articles was quite low, it generally increased with each
turker's successive pass. Also it's worth noting that errors that we
deliberately inserted in the starting paragraph as well as in real time
were swiftly edited out. Not much quantitative analysis of this
collaboration data has been done yet, though there are plans to conduct
more scientific experiments in the&amp;nbsp;future. &lt;/p&gt;

&lt;p&gt;Several other researchers have been conducting interesting studies on
mturk. Greg Little's work at MIT generated an interesting project called
&lt;a href="http://groups.csail.mit.edu/uid/turkit/"&gt;TurKit&lt;/a&gt;, intended to simplify setting up experiments such as the one
outlined above. Panos Ipeirotis at NYU runs a variety of turk
experiments as well as an &lt;a href="http://hyperion.stern.nyu.edu/mturk/"&gt;mturk statistics monitor&lt;/a&gt;, which
continually scrapes Mechanical Turk and generates summary data. Most
recently, Jennifer Boriss surveyed the Turk community about their
browser preferences &lt;a href="http://jboriss.wordpress.com/2010/01/13/mechanical-turk-studies-show-ie-users-discontent-a-growing-interest-in-chrome/"&gt;projecting a growing interest in Chrome&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;These varied Mechanical Turk projects represent only a small fraction of
the potential of crowd-sourced marketplaces. It's especially interesting
to take complex tasks, break them down into turk-sized morsels, and
recombine them again. To improve the article collaboration scenario
discussed here, one could provide an outline of an article and task
turkers to elaborate on each point. This seems to be exactly what Greg's
group is doing in an &lt;a href="http://groups.csail.mit.edu/uid/deneme/?p=603"&gt;collaborative essay writing experiment&lt;/a&gt;.  Such
an approach may also be effectively applicable to crowd-sourced software
development, which I hope to explore in the near future. Do you know
other interesting projects and resources related to Mechanical Turk? If
so, please respond&amp;nbsp;below!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/crowdsourcing-articles-with-mechanical-turk</feedburner:origLink></entry><entry><title>Ubiquitous Drums</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/EnMCjwiwPto/ubiquitous-drums" /><updated>2009-11-18T14:22:15Z</updated><published>2009-11-18T14:22:15Z</published><id>http://smus.com/ubiquitous-drums</id><content type="html">
      
&lt;h1&gt;
  Ubiquitous&amp;nbsp;Drums
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Nov. 18, 2009"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Nov. 18, 2009&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Look at the riders of any city bus. Many of them are plugged into their music
players, tapping away to the beat. I propose to augment our natural love of
rhythm into a ubiquitous wearable drum system. The target user of this system
isn’t only the typical rhythm loving bus rider, but also an amateur drummer.
Drum kits are heavy and unwieldy, making them difficult to transport to a jam
session. The proposed system can also act as a stand-in for a full drum-kit for
quick, impromptu&amp;nbsp;jamming.&lt;/p&gt;

&lt;p&gt;I took a pair of jeans and imbued them with two &lt;a href="http://www.sparkfun.com/commerce/product_info.php?products_id=9376"&gt;force-sensitive resistors&lt;/a&gt;,
one on each knee. The left pocket houses a sparkfun box containing an arduino
and a breadboard. Wires run through the pant legs to connect the pads to the
box. Wiring the pants was surprisingly easy, since as I discovered, electric
tape easily adheres to&amp;nbsp;denim.&lt;/p&gt;

&lt;iframe title="YouTube video player" width="600" height="368"
  src="http://www.youtube.com/embed/HjWx9fp-8oU" frameborder="0"
  allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;The two FSRs are hooked into pull-down switches which connect to analog ports
of the Arduino. Every time a pad is hit, Arduino sends the pad
ID and the force of the impact through the serial port. A python program running on my machine
listens on the serial port and synthesizes sounds corresponding to the data
using &lt;a href="http://pyserial.sourceforge.net/"&gt;pyserial&lt;/a&gt; and &lt;a href="http://www.pygame.org/news.html"&gt;pygame&lt;/a&gt;&amp;nbsp;respectively.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import pygame, sys, serial

# initialize the pygame mixer
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=256)

# map positions to sounds
samples = {
    'lknee': pygame.mixer.Sound('media/hihat-open.aif'),    
    #...
}

# initialize serial port
s = serial.Serial('/dev/tty.usbserial-A6008ea9', 9600)

while 1:
    # read from the serial port
    line = s.readline().strip()
    # ...
    sample = samples[pad_id]
    sample.play()

if __name__ == '__main__':
    pass
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This first prototype of Drum Pants is intentionally crude. Aside from
increasing this system’s production value, there are a number of limitations
that should be addressed. The current prototype requires a computer to
synthesize sounds, which greatly hinders portability. By retrofitting the
Arduino with a &lt;a href="http://asynclabs.com/wiki/index.php?title=WiShield_1.0"&gt;wifi shield&lt;/a&gt;, the system could communicate with any
wifi-capable synthesizer, such as an Android&amp;nbsp;phone.&lt;/p&gt;

&lt;p&gt;Another issue with this system is that it’s built entirely into a pair of
pants. This makes putting drum pads into other items of clothing impossible. To
address this problem, the pads could wirelessly communicate to the Arduino
device. In this case, the pads would be self-contained transmitters that could
be placed anywhere. This opens up a wide variety of applications, such as
placing the pad onto a pair of shoes to simulate a kick or hi-hat pedal. Do you
like this idea? Please give me feedback&amp;nbsp;below!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Do you want to build your own drum pants? Check out this
&lt;a href="http://www.instructables.com/id/Drum-Wear-drums-in-your-clothing/"&gt;instructable&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update 2:&lt;/strong&gt; Accepted as a CHI 2010 WIP. Many thanks to &lt;a href="http://code.arc.cmu.edu/~mdg/"&gt;Mark Gross&lt;/a&gt;! 
&lt;a href="/media/images/blog/2009/wip462-smus.pdf"&gt;Extended abstract&lt;/a&gt; now&amp;nbsp;available.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/ubiquitous-drums</feedburner:origLink></entry><entry><title>Arduino-Nerf Mashup</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/19wcxEcIkEU/arduino-nerf" /><updated>2009-10-20T20:48:54Z</updated><published>2009-10-20T20:48:54Z</published><id>http://smus.com/arduino-nerf</id><content type="html">
      
&lt;h1&gt;
  Arduino-Nerf&amp;nbsp;Mashup
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Oct. 20, 2009"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Oct. 20, 2009&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;My favorite class at CMU is probably &lt;a href="http://mtifall09.wordpress.com/"&gt;Making Things Interactive&lt;/a&gt;. For me
it is an opportunity to take my thus far casual electronics hacking to to
the next level. In this article, I'll briefly outline my submission for the
"Motion" assignment. I used a servo motor to control a Nerf gun. I built it
and installed it in my MHCI lab, which has a handful of Nerf pistols
floating around. The idea was to have the gun automatically fire at
unsuspecting visitors as they entered the&amp;nbsp;room. &lt;/p&gt;

&lt;p&gt;To arm it, one manually cocks the gun, loads a dart and resets the program
by pushing the button on the Arduino board. The program then allows 10
seconds for the door to be open before it arms the system. When the system
is armed, the servo activates and shoots the gun as soon as the door is&amp;nbsp;opened.&lt;/p&gt;

&lt;iframe title="YouTube video player" width="600" height="480"
  src="http://www.youtube.com/embed/-XkLRZ2OBRo" frameborder="0"
  allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;As you can see, I generously used rubber bands and binder clips in this
project. I used them to fasten the servo motor to the Nerf gun. I also used
them to harness a telephone cable by wrapping a rubber band around the RJ11
connector, carefully inserting jumpers, and applying additional pressure (to
ensure contact) with binder clips. This hacked telephone cable stretched
from the gun to the door&amp;nbsp;sensor. &lt;/p&gt;

&lt;p&gt;The circuit was dead simple.  The door acted as a switch for the pull down
resistor&amp;nbsp;circuit. &lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/arduino-nerf</feedburner:origLink></entry><entry><title>New Design</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/5Dy8vaFLOx8/new-design" /><updated>2009-09-28T10:31:29Z</updated><published>2009-09-28T10:31:29Z</published><id>http://smus.com/new-design</id><content type="html">
      
&lt;h1&gt;
  New&amp;nbsp;Design
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Sept. 28, 2009"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Sept. 28, 2009&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Since starting this site, I've been using a lightly tweaked version of the
&lt;a href="http://wp-themes.com/clockworkair/"&gt;ClockWorkAir&lt;/a&gt; theme. The old design featured a prominent quote in prime
screen real estate and a tag cloud in the upper sidebar. A blue title bar,
reminiscent of the default wordpress theme, graced the blog header. The main
text was small and there was hardly enough room to place images in the left
margin. Here was my old blog&amp;nbsp;design:&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2009/old_design.png" alt="old" /&gt;&lt;/p&gt;

&lt;p&gt;As I had hoped, the design class I'm taking at CMU gave me some good ideas.
Here's my new blog design. I'm much happier with it than with the former look,
but still have a backlog of things to tweak. My design was inspired by several
&lt;a href="http://limi.net/"&gt;people's&lt;/a&gt; &lt;a href="http://www.joehewitt.com/"&gt;web logs&lt;/a&gt;, which bring together form and function in a way
which appeals to me. Please give me constructive feedback on the new design if
you have time. I will release the wordpress theme if anyone wants it — I just
haven't had time to bundle it yet. Here is my new blog&amp;nbsp;design:&lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2009/new_design.png" alt="new" /&gt;&lt;/p&gt;

&lt;p&gt;As of this month it's been a year since my first blog post. Surprisingly, I've
been writing one to two blog entries per month and hope to keep my musings
flowing at approximately the same rate in the future. It's very rewarding as a
writer to have a steady flow of visitors, some recurring readership, and even
the odd discussion here or there. Thank you&amp;nbsp;all.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/new-design</feedburner:origLink></entry><entry><title>Offline Web Apps on the iPhone</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/_6KDYK4QO8o/offline-web-iphone" /><updated>2009-09-06T15:21:19Z</updated><published>2009-09-06T15:21:19Z</published><id>http://smus.com/offline-web-iphone</id><content type="html">
      
&lt;h1&gt;
  Offline Web Apps on the&amp;nbsp;iPhone
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Sept. 6, 2009"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Sept. 6, 2009&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;In the midst of my graduate studies, I somehow found the time to write a
simple prototype for a mobile Guitar Unleashed client. It's more of a
proof-of-concept for some cool new technologies that I've been meaning
to play with. Two things led me down this&amp;nbsp;path:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Since I'm no longer bound by corporate affiliation, I feel compelled
to finally develop an interesting application for&amp;nbsp;iPhone.&lt;/li&gt;
&lt;li&gt;I'm very bad at remembering guitar chords and lyrics, but never
bother making a cheat sheet to take to the campfire. I nearly always
have my phone in my pocket,&amp;nbsp;though.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The GU mobile prototype is very simple. Users can manage a list of
favorite songs, and view their lyrics with chords. At first, I set out
to create a native iPhone application to do this, but several
considerations made me change my&amp;nbsp;mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I don't want to deal with a potential App Store rejection, keeping
in mind the dubious legality of&amp;nbsp;lyrics&lt;/li&gt;
&lt;li&gt;The simplicity and data-centric nature of the app lends itself well
to a web&amp;nbsp;implementation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2009/phone.png" class="left"&gt;&lt;/p&gt;

&lt;p&gt;There are a few interesting technical challenges to overcome in implementing
this application. Firstly, the app needs to blend visually with the iPhone look
and feel. I chose the &lt;a href="http://code.google.com/p/iui/"&gt;iUI&lt;/a&gt; web framework to develop my application. In
retrospect, &lt;a href="http://www.jqtouch.com/"&gt;jQTouch&lt;/a&gt; would have probably been a better choice, since I ended
up using jQuery. In addition, iUI is very much not bug free, and I don't like
iUI's anchor-based navigation. Apple provides several iPhone-specific meta tags
to enhance the web experience. For example, &lt;code&gt;&amp;lt;link rel="apple-touch-icon"
href="logo-touch-icon.png" /&amp;gt;&lt;/code&gt; allows to specify the icon that will appear in
the iPhone springboard. Setting &lt;code&gt;&amp;lt;meta name="apple-touch-fullscreen"
content="YES" /&amp;gt;&lt;/code&gt; will remove the lower hud of MobileSafari. The full list of
options is available &lt;a href="http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html"&gt;from Apple&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The second broad challenge for mine and iPhone web applications in general is
that they need to remain functional without an internet connection. For my
mobile client, data would be provided by two calls returning JSON:
&lt;code&gt;/song/search?query=myQuery&lt;/code&gt; returning a list of songs and &lt;code&gt;/song/get/myID&lt;/code&gt;
returning specific song info. My first instinct was to implement local
persistence via cookies, as is customary in web development. There's a 4K limit
on the size of each cookie, so storing all songs in one cookie was out of the
question. A simple alternative was to store a cookie with an array of song IDs,
and a cookie for each song. This strategy worked quite well on WebKit and in
the iPhone simulator. Unfortunately, cookie persistence works differently on
the actual iPhone, likely for security reasons. Every time the phone reboots,
MobileSafari's cookie jar is&amp;nbsp;emptied. &lt;/p&gt;

&lt;p&gt;The alternative to this is a much more modern approach: &lt;a href="http://webkit.org/blog/126/webkit-does-html5-client-side-database-storage/"&gt;HTML5 databases&lt;/a&gt;.
WebKit now allows you to store structured data locally in an SQLite database. I
found this approach to work very well on iPhone. The database backend persists
through reboots, as expected. I implemented both storage schemes in separate
files, available for your scrutiny: the class SongJar for cookies in &lt;a href="/x/offline-mobile/jar.js"&gt;jar.js&lt;/a&gt;
and SongDatabase for HTML5 storage &lt;a href="/x/offline-mobile/database.js"&gt;database.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The last piece of the puzzle is how to force the web application's source files
to get cached on the iPhone, so that the app remains accessible even when the
phone is offline. This is done with a &lt;a href="http://www.w3.org/TR/2009/WD-html5-20090212/offline.html"&gt;cache manifest&lt;/a&gt;, also new in HTML5. I
ran into several problems trying to set this up, and would have benefited from
these&amp;nbsp;tips:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reference the manifest from the HTML with
&lt;code&gt;&amp;lt;html manifest="cache-manifest"&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Serve the manifest with the &lt;code&gt;text/cache-manifest&lt;/code&gt; mime&amp;nbsp;type&lt;/li&gt;
&lt;li&gt;Ensure that all paths in the manifest are&amp;nbsp;accessible&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Though my app is a mere prototype, I'm quite happy with the result.
After adding the application to the springboard, it &lt;em&gt;almost&lt;/em&gt; feels like
a first class iPhone application. You can try it out at
&lt;a href="http://www.guitarunleashed.com/m/"&gt;http://www.guitarunleashed.com/m/&lt;/a&gt;, and please bear in mind that it's
a proof of&amp;nbsp;concept.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/offline-web-iphone</feedburner:origLink></entry><entry><title>Never Delete Your Gmail Account</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/DatUYDC7TUw/gmail-rant" /><updated>2009-08-28T20:36:29Z</updated><published>2009-08-28T20:36:29Z</published><id>http://smus.com/gmail-rant</id><content type="html">
      
&lt;h1&gt;
  Never Delete Your Gmail&amp;nbsp;Account
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Aug. 28, 2009"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Aug. 28, 2009&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Since moving to the states, I have had nothing but grief from the .ca at the
end of my all-purpose email address. Even in Canada, people would constantly
confuse z3.ca for z3.com, resulting in email bounces. To resolve this problem
once and for all, I decided to switch to Gmail like all the cool kids. I
registered boris.smus long ago out but never used it. The first thing I did was
try to link my z3.ca to the Gmail. When I couldn't figure out how to do that, I
deleted my Gmail account in order to re-create a new pre-linked account with
the same name. Sounds innocent enough,&amp;nbsp;right? &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Wrong&lt;/em&gt;! Google has an uncharacteristically evil account deletion policy which
is not at all clearly communicated. The deletion page simply&amp;nbsp;says:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You're trying to delete your Google Account that provides access to
  the Google products listed below. Please select each checkbox to
  confirm you fully understand that you'll no longer be able to use any
  of these products and all information associated with them, and that
  your account will be&amp;nbsp;lost.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Meanwhile, the &lt;a href="http://www.google.com/support/accounts/bin/answer.py?hl=en&amp;amp;answer=32046"&gt;F.A.Q.&lt;/a&gt;&amp;nbsp;reads:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you use Gmail with your account, you'll no longer be able to access
  that email. You'll also be unable to reuse your Gmail&amp;nbsp;username.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Long story short, I tried everything in my power to recover the old username. I
found handfuls of frustrated users in the same position as me; some having
deleted their account by accident, others victims of pranks and identity theft.
I asked on official and unofficial Gmail forums, and even consulted with my
Googler friends, all to no avail.  After mourning the loss of
boris.smus@gmail.com, it was time to take a critical look at Google's email&amp;nbsp;offerings. &lt;/p&gt;

&lt;p&gt;I enjoy Gmail's webmail client very much. It's a fast, intuitive, search and
tag based model with virtually unlimited mailbox storage. In terms of
usability, however, I much prefer Mail on Mac. On the iPhone, the native Mail
client is far superior to the mobile web Gmail client. Fortunately, Google
provides SMTP and IMAP services to fill this need. Sadly, both of these
services are plagued with&amp;nbsp;issues. &lt;/p&gt;

&lt;p&gt;I recently sent an email which had roughly 50 bcc recipients. Google's SMTP
server thought I was a spammer and banned me, &lt;em&gt;despite my having authenticated
via SSL&lt;/em&gt;.  Perhaps sending email to 50 people is slightly unusual. Still, I
would expect my mail gateway to be capable of performing such a&amp;nbsp;'feat'. &lt;/p&gt;

&lt;p&gt;The way the Google IMAP maps directories is fundamentally incompatible with
Mail.app expectations. Further, the IMAP server has a limit of 10 simultaneous
connections, which often causes the connection threshold to be reached with
&lt;a href="http://mail.google.com/support/bin/answer.py?hl=en&amp;amp;answer=97150"&gt;just two connected clients&lt;/a&gt;. Additionally, the IMAP server is often down; I
wish I had some data to support that, but I&amp;nbsp;don't. &lt;/p&gt;

&lt;p&gt;When I finally settled on boris@borismus.com, I had a decision to make: do I
use Google Apps or Webfaction for email? I quite like Webfaction, and, perhaps
irrationally, trust them more than Google with my private data. With the above
limitations of Gmail in mind, I did not hesitate to choose Webfaction. I still
miss having a first.last@gmail.com though. What's with the oddly draconian
account deletion&amp;nbsp;rules?&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/gmail-rant</feedburner:origLink></entry><entry><title>Plotting Something Radial</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/NU8PElmqesY/nxt-plotter" /><updated>2009-08-07T13:00:17Z</updated><published>2009-08-07T13:00:17Z</published><id>http://smus.com/nxt-plotter</id><content type="html">
      
&lt;h1&gt;
  Plotting Something&amp;nbsp;Radial
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Aug. 7, 2009"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Aug. 7, 2009&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;In anticipation of 48-739: Making Things Interactive, I've been itching to
build something interesting. I decided to create a printing plotter out of my
brother's Mindstorms set. There are already many &lt;a href="http://www.norgesgade14.dk/plotter.php"&gt;excellent plotter designs&lt;/a&gt;
floating around in the Mindstorms community, so I decided to try something new.
Plotters typically draw straight, edge-aligned lines, since they have a caret
motor which travels along the x-axis and a feed motor which aligns along the
y-axis. There are many variations on this&amp;nbsp;theme. &lt;/p&gt;

&lt;p&gt;What if, instead of using the Cartesian coordinate system, a plotter was built
against radial coordinates. That is, one motor would control the rotation of an
arm (theta in radian coordinates), and one would drive a caret along the arm,
controlling the distance from the origin (r in radian coordinates). I began
building, and despite the ardent help of my young brother, we failed to create
a reasonable construction. The problem we ran into was that as r increased to
the maximum length of the arm, the engine driving theta would not have enough
strength to rotate due to the increased torque. I'm ashamed to admit that we
gave&amp;nbsp;up. &lt;/p&gt;

&lt;p&gt;A month later, I picked up the project again, and due to limited time, decided
to build a Cartesian plotter after all.  I did not consult state of the art of
NXT plotters, and ended up with a plotter of unconventional design. Instead of
a paper feed, a caret travels along the y-axis, and another caret travels along
the first caret on the x-axis. I borrowed wheels from the RCX set, but the rest
is stock&amp;nbsp;NXT. &lt;/p&gt;

&lt;iframe title="YouTube video player" width="600" height="368"
  src="http://www.youtube.com/embed/tZhqjrHSIfE" frameborder="0"
  allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;I named the robot "Malevich 2" in honor of &lt;a href="http://en.wikipedia.org/wiki/Kazimir_Malevich"&gt;Kazimir Malevich&lt;/a&gt;, an avant-guard
Russian painter, famous for pioneering geometric abstract art, especially a
series of &lt;a href="http://en.wikipedia.org/wiki/File:Malevich.black-square.jpg"&gt;paintings of squares&lt;/a&gt;. I prefer Malevich 2's rendition of the
square, but maybe that's just&amp;nbsp;me.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/nxt-plotter</feedburner:origLink></entry><entry><title>Extending Google Wave Terminology</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/vHG7XxzjdCQ/extending-google-wave-terminology" /><updated>2009-07-22T23:24:12Z</updated><published>2009-07-22T23:24:12Z</published><id>http://smus.com/extending-google-wave-terminology</id><content type="html">
      
&lt;h1&gt;
  Extending Google Wave&amp;nbsp;Terminology
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="July 22, 2009"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;July 22, 2009&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;For the last few days, I've had the chance to get my feet wet in the
&lt;a href="https://services.google.com/fb/forms/wavesignupfordev/"&gt;developer sandbox&lt;/a&gt; of Google Wave. My first impressions are very
positive. I am as awed now by the scope and potential impact of Wave as
I was after watching the hour long &lt;a href="http://wave.google.com/"&gt;video from Google I/O&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;As consolation to those itching to &lt;em&gt;try it already&lt;/em&gt;, bear in mind that the
sandbox is in a very raw state right now. Performance issues aside, informative
text such&amp;nbsp;as:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This wave is experiencing some slight turbulence, and may explode. If
  you don't wanna explode, please re-open the wave. Some recent changes
  may not be&amp;nbsp;saved.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;span class="dquo"&gt;"&lt;/span&gt;Everything's shiny, Cap'n. Not to fret!" Unfortunately, you'll need
  to &lt;em&gt;refresh&lt;/em&gt;. Wanna tell Dr. Wave what&amp;nbsp;happened?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;appear quite often. Of course, this is totally expected for pre-alpha
software -- I'm merely pointing out the humorous&amp;nbsp;messages!&lt;/p&gt;

&lt;p&gt;For a more intimate look into Wave, especially some of the deeper, more social
issues, here is part of an ongoing ha-ha-only-serious joke from the internal
wave-discuss group about extending existing &lt;a href="http://mashable.com/2009/05/28/google-wave-guide/"&gt;Wave terminology&lt;/a&gt;. The list is
really telling, since indeed, I have read many ridiculous Drips and most of my
fresh waves Surge only to get Borked by&amp;nbsp;rickrolley.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Wavejack&lt;/strong&gt; -- To hijack the contents of a wave to where it no longer resembles the original&amp;nbsp;idea&lt;/li&gt;
&lt;li&gt;&lt;em&gt;* Drip&lt;/em&gt;* -- Stupid question no one&amp;nbsp;answers&lt;/li&gt;
&lt;li&gt;&lt;em&gt;* Bork&lt;/em&gt;* -- To add useless, noisy or destructive bots to a wave. (aka The Swedish Chef or rickrolley or the borkforceone&amp;nbsp;bot)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;* Surge&lt;/em&gt;* -- The effect of a fresh wave that elicits the chaos of mass&amp;nbsp;editing&lt;/li&gt;
&lt;li&gt;&lt;em&gt;* Drown&lt;/em&gt;* -- To have so many waves to follow that one can't keep up with&amp;nbsp;them &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sea Sick&lt;/strong&gt; -- A state of dizziness induced by an overly active&amp;nbsp;wave &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tuna/Noise/Herring&lt;/strong&gt; -- Wave speak for&amp;nbsp;Spam&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fishnet&lt;/strong&gt; -- Spam&amp;nbsp;filter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Despite instabilities and widespread antisocial behavior, the wave sandbox is
alive with a flurry of activity. People are busy creating annoying bots,
forming role playing communities and writing collaborative books. So Google has
a lot of issues to work out, not in the least how to stop us all from drowning&amp;nbsp;:) &lt;/p&gt;

&lt;p&gt;So far, I haven't had a chance to write any interesting wave extensions, but
it's in my things to&amp;nbsp;do!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/extending-google-wave-terminology</feedburner:origLink></entry><entry><title>Generating Guitar Chord Diagrams</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/oo-8xgyGR_g/generating-guitar-chord-diagrams" /><updated>2009-06-14T16:42:57Z</updated><published>2009-06-14T16:42:57Z</published><id>http://smus.com/generating-guitar-chord-diagrams</id><content type="html">
      
&lt;h1&gt;
  Generating Guitar Chord&amp;nbsp;Diagrams
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="June 14, 2009"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;June 14, 2009&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;One day I wanted to add a feature to &lt;a href="http://www.guitarunleashed.com"&gt;Guitar Unleashed&lt;/a&gt; which exists
in some of the better guitar tab sites. When a user hovers over a chord,
they are shown a diagram representing the guitar fret with overlaid
finger positions required to produce this chord. Many of the most
popular sites do this by showing a crude, plain-text representation of
the&amp;nbsp;chord.&lt;/p&gt;

&lt;p&gt;For example, a C chord is shown as&amp;nbsp;follows:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;e ---|---|---|---|---|
B -x-|---|---|---|---|
G ---|---|---|---|---|
D ---|-x-|---|---|---|
A ---|---|-x-|---|---|
E ---|---|-o-|---|---|
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Typeset guitar chord representation looks very different. Two most
popular variants of the C chord would appear above the staff with small
symbols that look something like&amp;nbsp;this: &lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2009/C.png" alt="C" title="C" /&gt;
&lt;img src="/media/images/blog/2009/C_1.png" alt="C_1" title="C_1" /&gt; &lt;/p&gt;

&lt;p&gt;Such images have many benefits as compared to the plain text&amp;nbsp;version:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Convention: many guitar players are used to learning from sheet
music, so this chord notation is familiar to&amp;nbsp;them.&lt;/li&gt;
&lt;li&gt;Readability: the image representation is more compact and more
pleasant to&amp;nbsp;read.&lt;/li&gt;
&lt;li&gt;Completeness: the image version has an easy way to display barre
chords and specify fingerings (not&amp;nbsp;pictured)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;True to Guitar Unleashed's mission of being a truly usable guitar chords
site, I decided to display typeset fret diagrams. For the
implementation, a program would pre-generate images on the server based
on the encoded chord shape and then serve these images dynamically via
JavaScript. Unfortunately, I found no software to generate such images
for all popular chords, so I ended up developing my&amp;nbsp;own. &lt;/p&gt;

&lt;p&gt;From some brief
research, I decided that &lt;a href="http://lilypond.org/doc/v2.9/Documentation/user/lilypond/Fret-diagrams"&gt;lilypond fret diagrams&lt;/a&gt; are the most elegant
way of creating such images. The alternative was to use &lt;a href="http://www.aei.mpg.de/~peekas/gchords/"&gt;GCHORDS&lt;/a&gt;
whose output I liked less, and which required depending on TeX.
Typically lilypond typesets an entire piece of sheet music on a staff,
with clefs, key signatures, etc. After discovering a really great
&lt;a href="http://netcetera.org/cgi-bin/tmbundles.cgi#Lilypond"&gt;lilypond bundle for textmate&lt;/a&gt;, I managed to get rid of these
unnecessary features and display just a fret diagram. The following
python-parametrized lilypond template does the&amp;nbsp;trick:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;\include "lilypond-book-preamble.ly"
\version "2.10.0"
\markup
\fret-diagram-terse #"%(chord_markup)s"
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, using lilypond's fret diagram encoding, a python script replaces
&lt;code&gt;%(chord_markup)s&lt;/code&gt; with the desired markup. Lilypond's markup is very
terse but mostly manageable. Sample markup for a C chord looks like
this: &lt;code&gt;x;3;2;o;1;o;&lt;/code&gt;, while the barre version looks a little bit more
bizarre, like this: &lt;code&gt;3-(;3;5;5;5;3-);&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;My python program contains a list of chord shapes for the most popular chords,
encoded in the manner above, then outputs images for each chord and variant
into a directory.  I've made it &lt;a href="/x/chord-image-generator.zip"&gt;available for download&lt;/a&gt;, in case your next
project needs generated fret diagrams with a highly customizable look. You can
also see the diagrams live at &lt;a href="http://www.guitarunleashed.com"&gt;guitarunleashed.com&lt;/a&gt;.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/generating-guitar-chord-diagrams</feedburner:origLink></entry><entry><title>Carnegie Mellon University!?</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/ViOFpnYg_Ic/carnegie-mellon-university" /><updated>2009-04-18T12:47:07Z</updated><published>2009-04-18T12:47:07Z</published><id>http://smus.com/carnegie-mellon-university</id><content type="html">
      
&lt;h1&gt;
  Carnegie Mellon&amp;nbsp;University!?
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="April 18, 2009"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;April 18, 2009&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;In late March, I was completely thrilled to hear that the [Carnegie
Mellon University][] &lt;a href="http://www.hcii.cmu.edu/"&gt;Human Computer Interaction Institute&lt;/a&gt; accepted
me into their &lt;a href="http://www.hcii.cmu.edu/masters-program"&gt;Master&lt;/a&gt;'s program! In addition to admission, they
offered a very juicy scholarship to spend part of the time on the
beautiful island of &lt;a href="http://en.wikipedia.org/wiki/Madeira"&gt;Madeira&lt;/a&gt;. CMU was my most far fetched reach
school, so I attribute this wonderful fortune to a clerical error made
by the admission&amp;nbsp;committee. &lt;/p&gt;

&lt;p&gt;In addition to studying at one of the best
universities in the world, traveling to Europe, and relaxing on the
beach, here are a couple of reasons why my inner geek can't contain his
excitement, try as he might. These incredible courses are offered at CMU
in September&amp;nbsp;2009:&lt;/p&gt;

&lt;h3&gt;Making Things&amp;nbsp;Interactive:&lt;/h3&gt;

&lt;p&gt;In this hands-on design-build class you will learn the skills to embed
sensors and actuators (light, sound, touch, motion, etc.) into everyday
things (and places etc.) and to program their interactive behavior using
a microcontroller. Through weekly exercises and a term project the class
will introduce basic analog electronics and microcontroller programming,
as well as exploration into using kinetics and materials to make the
things you design perform. Emphasis will be on creating innovative
experiences using simple robotic technologies. The graduate edition of
this course will require additional work including a paper that can be
submitted to a peer-reviewed interaction design conference such as CHI,
UIST, or TEI.Students from all disciplines are welcome: but please note
that the class demands that you master technical material. Experience in
at least one of: programming, electronics, or physical fabrication is
strongly&amp;nbsp;recommended.&lt;/p&gt;

&lt;h3&gt;Principles of Human-Robot&amp;nbsp;Interaction:&lt;/h3&gt;

&lt;p&gt;This course focuses on the emerging field of human-robot interaction,
bringing together research and application of methodology from robotics,
human factors, human-computer interaction, interaction design, cognitive
psychology, education and other fields to enable robots to have more
natural and more rewarding interactions with humans throughout their
spheres of functioning. This course is a combination of state-of-art
reading and discussions, focused team exercises and problem-solving
sessions in human-robot interaction, and a special team project
resulting in the implementation of a human-robot interaction&amp;nbsp;system.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/carnegie-mellon-university</feedburner:origLink></entry><entry><title>Guitar Unleashed</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/fWOMWoQ6LO0/guitar-unleashed" /><updated>2009-03-23T23:01:24Z</updated><published>2009-03-23T23:01:24Z</published><id>http://smus.com/guitar-unleashed</id><content type="html">
      
&lt;h1&gt;
  Guitar&amp;nbsp;Unleashed
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="March 23, 2009"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;March 23, 2009&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;I've been collaborating with my dad on an experimental web-based guitar chord
editing service. It's still a work in progress, but we are ready to launch a
beta version. Please visit &lt;a href="http://www.guitarunleashed.com"&gt;http://www.guitarunleashed.com/&lt;/a&gt; to check it out
and provide&amp;nbsp;feedback. &lt;/p&gt;

&lt;p&gt;We hope that the major features of the service are easily discoverable, but in
case they are not, here is a&amp;nbsp;list:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add chords to your lyrics by clicking on any&amp;nbsp;character&lt;/li&gt;
&lt;li&gt;Move chords via drag and&amp;nbsp;drop&lt;/li&gt;
&lt;li&gt;Search for songs created by other users or from external&amp;nbsp;sources&lt;/li&gt;
&lt;li&gt;Save your work and share it with&amp;nbsp;others&lt;/li&gt;
&lt;li&gt;Add songs that you like to your favorites&amp;nbsp;list&lt;/li&gt;
&lt;li&gt;Already have lyrics with chords? Create a new song, paste lyrics
with chords in and see the chords come to&amp;nbsp;life.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please note that the application relies heavily on JavaScript, so it needs to
be enabled in your&amp;nbsp;browser.&lt;/p&gt;

&lt;h2&gt;Known&amp;nbsp;issues&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Some lyrics may display incorrectly. This is an issue with
&lt;a href="http://lyricwiki.org/Main_Page"&gt;LyricWiki.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Long chords placed at the beginning or the end of lyrics line may
extend outside of the editing&amp;nbsp;area&lt;/li&gt;
&lt;li&gt;Long lines in lyrics are not handled&amp;nbsp;elegantly&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Future&amp;nbsp;features&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Editing lyric&amp;nbsp;text&lt;/li&gt;
&lt;li&gt;Commenting on&amp;nbsp;songs&lt;/li&gt;
&lt;li&gt;Searching sources of lyrics and chords other than&amp;nbsp;LyricWiki&lt;/li&gt;
&lt;li&gt;Song&amp;nbsp;books&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please give us feedback! Use the feedback button on guitarunleashed.com or
contact us directly at &lt;a href="mailto:feedback@guitarunleashed.com"&gt;feedback@guitarunleashed.com&lt;/a&gt;&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/guitar-unleashed</feedburner:origLink></entry><entry><title>Prettifying Embedded YouTube</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/ePQ5tBLizGY/prettifying-embedded-youtube" /><updated>2009-02-27T22:16:44Z</updated><published>2009-02-27T22:16:44Z</published><id>http://smus.com/prettifying-embedded-youtube</id><content type="html">
      
&lt;h1&gt;
  Prettifying Embedded&amp;nbsp;YouTube
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Feb. 27, 2009"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Feb. 27, 2009&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;In late January 2009, &lt;a href="http://www.youtube.com/"&gt;YouTube&lt;/a&gt; decided to change the default look of their
embedded videos. They silently added an informative header which includes the
video title and rating. Some time before then, a default search bar appeared at
the top of the video. Thanks to these changes, most haphazardly embedded
YouTube videos on the internet sport a repulsive new&amp;nbsp;look. &lt;/p&gt;

&lt;p&gt;What, you might ask, can be done about this excessive ugliness? Well, you could
switch to &lt;a href="http://www.vimeo.com"&gt;Vimeo&lt;/a&gt;, which has a much nicer set of defaults, but that would
mean bidding the YouTube community farewell -- and what a tragedy that would
be! A better alternative is to learn the &lt;a href="http://code.google.com/apis/youtube/player_parameters.html"&gt;YouTube Embedding API&lt;/a&gt;, but there
are still&amp;nbsp;problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Embedding code for YouTube videos is&amp;nbsp;ugly.&lt;/li&gt;
&lt;li&gt;The YouTube embedding scheme often silently&amp;nbsp;changes.&lt;/li&gt;
&lt;li&gt;If you embed multiple videos, there is no way to specify a default
set of embedding&amp;nbsp;preferences.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are a wordpress user, all of these issues are resolved by the most
excellent &lt;a href="http://www.prelovac.com/vladimir/wordpress-plugins/smart-youtube"&gt;Smart YouTube&lt;/a&gt; plugin. With it you can embed videos by simply
inserting the URL to any YouTube video into your wordpress page or post and
replacing http with http&lt;strong&gt;v&lt;/strong&gt;. Through a settings page, you can modify the way
all of your embedded videos look with one fell&amp;nbsp;swoop.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/prettifying-embedded-youtube</feedburner:origLink></entry><entry><title>Clean drag and drop upload in Safari</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/nV9g_Da0TpM/clean-drag-and-drop-upload-in-safari" /><updated>2009-02-15T00:25:50Z</updated><published>2009-02-15T00:25:50Z</published><id>http://smus.com/clean-drag-and-drop-upload-in-safari</id><content type="html">
      
&lt;h1&gt;
  Clean drag and drop upload in&amp;nbsp;Safari
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Feb. 15, 2009"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Feb. 15, 2009&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Somehow I often find myself arguing in defense of the web browser as a
viable platform for developing rich applications. In many such
discussions, the issue of interoperability with the desktop arises.
Someone will astutely observe that they &lt;strong&gt;can't even drag and drop&lt;/strong&gt;
from their OS file manager into their browser, and all hell will break&amp;nbsp;loose. &lt;/p&gt;

&lt;p&gt;Happily, this is changing! Since version 3, Safari on Mac OS X
has had support for &lt;a href="http://www.jakeri.net/2008/04/drag-and-drop-into-file-upload-in-safari/"&gt;dragging and dropping files&lt;/a&gt; from the finder into
file input boxes. In various kludgy ways, &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/2190"&gt;Firefox&lt;/a&gt; and &lt;a href="http://www.download.com/HTTP-File-Upload-ActiveX-Control/3000-2206_4-10451672.html"&gt;IE&lt;/a&gt; are now
following&amp;nbsp;suite. &lt;/p&gt;

&lt;p&gt;Unfortunately, even in Safari, the default look of the
&lt;code&gt;&amp;lt;input type="file"&amp;gt;&lt;/code&gt; box is quite ugly and the element itself is
&lt;a href="http://www.quirksmode.org/dom/inputfile.html"&gt;difficult to style&lt;/a&gt;. In addition, clicking anywhere in the file input
element causes the default open file dialog to appear. I wanted to
provide drag-and-drop uploading without ugly boxes or browser dialogs.
The solution I came up with involves hiding the file upload box entirely
by setting its opacity to 0, and then preventing the default action on
click via &lt;code&gt;event.preventDefault()&lt;/code&gt;. Here's a sample of what I mean, with
the entire browser window &lt;a href="/x/drag-drop-upload.html"&gt;converted into a drag area&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Note that the drag area must be the first DOM element to receive the drop event
for this approach to work. Unfortunately I ran into a bug where the file dialog
refuses to bubble click events to other elements below it. This is baffling to
me, since &lt;code&gt;event.preventDefault()&lt;/code&gt; should not stop event propagation, but only
prevent the default browser handler from being called. You can see what I mean
by trying to click the link in the &lt;a href="/x/drag-drop-upload.html"&gt;sample HTML&lt;/a&gt;
file. If this is not a bug, and someone has an answer, I would really
appreciate&amp;nbsp;it.&lt;/p&gt;

&lt;p&gt;Note also that there are &lt;a href="http://www.radinks.com/upload/"&gt;java applet&lt;/a&gt;-based drag and drop solutions, but
they are reserved for developers who have nothing but disdain for their&amp;nbsp;users.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/clean-drag-and-drop-upload-in-safari</feedburner:origLink></entry><entry><title>iPhone car kit: roll your own</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/ZWbpziLkk1c/the-best-iphone-car-kit" /><updated>2009-02-05T22:23:31Z</updated><published>2009-02-05T22:23:31Z</published><id>http://smus.com/the-best-iphone-car-kit</id><content type="html">
      
&lt;h1&gt;
  iPhone car kit: roll your&amp;nbsp;own
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Feb. 5, 2009"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Feb. 5, 2009&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Shortly after buying my first iPhone over a year ago, I found the right,
low-budget solution to my in-car music needs. The ingredients involved
are the following commonly found household&amp;nbsp;items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One Compact&amp;nbsp;Disk&lt;/li&gt;
&lt;li&gt;Some Duct&amp;nbsp;Tape&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea is to take the iPhone dock and stick it onto the CD with duct
tape. Next, jam the CD into a tight slot in the dash of your car (easy
to find for a Del Sol owner). Finally, attach power cables and/or AUX
audio jacks to the dock. The result is a flexible, shock-absorbent mount
for your everyone's favorite phone. I've used this hack for well over a
year now with no problems. Here's how it looks in the interior of my&amp;nbsp;car:&lt;/p&gt;

&lt;iframe title="YouTube video player" width="600" height="368"
  src="http://www.youtube.com/embed/Yg3VrsDXIpI" frameborder="0"
  allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;In a word:&amp;nbsp;awesome.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/the-best-iphone-car-kit</feedburner:origLink></entry><entry><title>Performance of Canvas versus SVG</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/kPZsTavJvBQ/canvas-vs-svg-performance" /><updated>2009-01-19T09:07:47Z</updated><published>2009-01-19T09:07:47Z</published><id>http://smus.com/canvas-vs-svg-performance</id><content type="html">
      
&lt;h1&gt;
  Performance of Canvas versus&amp;nbsp;SVG
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Jan. 19, 2009"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Jan. 19, 2009&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;At the core of the traditional HTML/CSS developer's toolkit is a set of nested
boxes describing offset, margin, border and padding, known as the box model.
Variations on the box theme are sufficient to describe most page layouts, but
in some complex applications, it's necessary to render something more
interesting, like diagonal lines, or polygons. There are currently two
relatively well-supported web graphics technologies: SVG and Canvas. There are
significant performance differences, however, which I would like to discuss in
this&amp;nbsp;article. &lt;/p&gt;

&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Svg"&gt;Scalable Vector Graphics&lt;/a&gt; (SVG) is by far the oldest of the two. It is a
declarative, graphical language used to describe geometrical primitives via DOM
elements. SVG was drafted in the late 90s, and the latest version of 
&lt;a href="http://www.w3.org/TR/SVG11/"&gt;SVG, version 1.1&lt;/a&gt; was finalized in 2003. It took 3 more years for it to be
incorporated into shipping versions of Mozilla Firefox and Safari.
Unfortunately, the length of the SVG adoption process caused the web
development community to seek other options. The &lt;a href="http://en.wikipedia.org/wiki/Canvas_(HTML_element)"&gt;HTML5 Canvas&lt;/a&gt; element was
introduced as a much simpler alternative to graphics on the web. It provides an
image-like graphics context which can be accessed via a set of javascript
calls, similar to a 2D subset of OpenGL. It was originally introduced by Apple
in WebKit builds, but is now supported in Mozilla Firefox as&amp;nbsp;well. &lt;/p&gt;

&lt;p&gt;I produced some metrics to compare the two technologies in terms of performance
by writing a Javascript program for &lt;a href="/x/canvas-svg-benchmark/"&gt;collecting performance data&lt;/a&gt;. This
program draws rows of circles onto a fixed-size drawing area in SVG and in
Canvas, and then compares how long various operations take. It also records the
duration to create the initial drawing context, to render the scene and to
clear the scene. A test runner invokes the benchmark with the following
variables: number of circles, drawing area dimensions and circle size. Each of
these variables are varied independantly and automatically resulting in the
following&amp;nbsp;observations: &lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2009/varying-number-of-objects.png" alt="Varying the number of objects" /&gt; &lt;/p&gt;

&lt;p&gt;Here are the results of the first fruitful experiment, which clearly shows that
SVG performance degrades quickly (exponentially on Safari?) in the number of
objects, but Canvas performance remains at a near-constant low. This makes
sense, since Canvas is just a bitmap buffer, while SVG has to maintain
additional references to each object that it renders. Also, though not
pictured, note that performance in clearing an SVG element also decreases in
the number of drawn&amp;nbsp;objects. &lt;/p&gt;

&lt;p&gt;&lt;img src="/media/images/blog/2009/varying-drawing-area-height.png" alt="Varying drawing area height" /&gt; &lt;/p&gt;

&lt;p&gt;When varying the size of the drawing area, canvas performance degrades
significantly, while SVG performance is completely unaffected.  Canvas
rendering performance seems to degrade linearly in the number of pixels in the
canvas area. Not pictured on the graph is clear performance for large canvases,
which also suffers linearly in pixel&amp;nbsp;count.&lt;/p&gt;

&lt;p&gt;I did not include the graph resulting from varying circle size as it had no
significant impact on render time. Another interesting observation is that
creating the canvas element takes a mysterious 10 ms on Firefox, but not on
Safari. This is not significant unless you are dealing with large numbers of
canvas&amp;nbsp;elements. &lt;/p&gt;

&lt;p&gt;Whether or not you use Canvas or SVG mostly depends on your specific
application. A graphics-intensive game, where many objects are redrawn all the
time is probably best implemented in Canvas. On the other hand, applications
like map viewers may involve large rendering areas and might lend themselves
better to&amp;nbsp;SVG. &lt;/p&gt;

&lt;p&gt;As always, please comment if something is unclear, inconsistent, boring or
omitted.&amp;nbsp;Thanks!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/canvas-vs-svg-performance</feedburner:origLink></entry><entry><title>iWork.com Feedback</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/c1qeYhslWRM/iworkcom-feedback" /><updated>2009-01-07T19:47:31Z</updated><published>2009-01-07T19:47:31Z</published><id>http://smus.com/iworkcom-feedback</id><content type="html">
      
&lt;h1&gt;
  iWork.com&amp;nbsp;Feedback
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Jan. 7, 2009"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Jan. 7, 2009&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;After spending a fair bit of time &lt;a href="http://search.twitter.com/search?q=iwork.com"&gt;monitoring twitter feeds&lt;/a&gt;, I was
pleasantly surprised by the &lt;a href="http://smokingapples.com/opinion/iworkcom-understanding-apples-online-office-extension/"&gt;world&lt;/a&gt;'s &lt;a href="http://www.appleinsider.com/articles/09/01/07/an_extensive_look_at_apples_new_iwork_com_service.html"&gt;response&lt;/a&gt; to the recently
announced &lt;a href="http://www.apple.com/iwork/iwork-dot-com/"&gt;iwork.com&lt;/a&gt;. There were some &lt;a href="http://www.engadget.com/2009/01/06/apple-announces-iwork-com-beta/"&gt;premature&lt;/a&gt; &lt;a href="http://www.gizmodo.com.au/2009/01/apple_sends_iwork_to_the_clouds_introduces_iworkcom.html"&gt;flames&lt;/a&gt;, but
mostly as a result of a fundamental misunderstanding of iwork.com's
purpose. Someone even gave a &lt;a href="%20http://ajaxian.com/archives/technical-details-behind-iworkcom/"&gt;technical breakdown&lt;/a&gt; of the application,
which is especially endearing to me as an&amp;nbsp;insider.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/iworkcom-feedback</feedburner:origLink></entry><entry><title>I See Dead CSS</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/4r_SKj4q6gE/i-see-dead-css" /><updated>2008-12-11T01:43:07Z</updated><published>2008-12-11T01:43:07Z</published><id>http://smus.com/i-see-dead-css</id><content type="html">
      
&lt;h1&gt;
  I See Dead&amp;nbsp;CSS
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Dec. 11, 2008"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Dec. 11, 2008&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;I wanted a tool that would analyze a complete web site, and report what
CSS selectors and IDs are dead. By dead, I mean one of two things --
either the ID or selector is referenced from the HTML and undefined in
the CSS or it is defined in the CSS but never referenced in the&amp;nbsp;HTML.&lt;/p&gt;

&lt;p&gt;A search for some CSS finding utilities proved somewhat fruitful. I
found a brief &lt;a href="http://www.aggiorno.com/blogs/aggiornings/post/Detecting-unused-CSS-selectors-.aspx"&gt;survey of related utilities&lt;/a&gt; for this finding or
cleaning dead CSS, the most promising of which was a Firefox plug-in
called &lt;a href="http://www.sitepoint.com/dustmeselectors/"&gt;Dust-Me Selectors&lt;/a&gt;. But I wanted something that could be
integrated into an automatic build process, easily invokable from the
command line without requiring a browser, so I started thinking about a
custom&amp;nbsp;solution.&lt;/p&gt;

&lt;p&gt;The problem can be solved as&amp;nbsp;follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find all referenced IDs and classes, called&amp;nbsp;R&lt;/li&gt;
&lt;li&gt;Find all defined IDs and classes, called&amp;nbsp;D&lt;/li&gt;
&lt;li&gt;Take a difference between the sets so that the list of undefined IDs
and classes is (R - D), and the list of unreferenced IDs and classes
is (D -&amp;nbsp;R)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I've already had the pleasure of using &lt;a href="http://www.crummy.com/software/BeautifulSoup/"&gt;BeautifulSoup&lt;/a&gt; python library
to parse all sorts of HTML documents, and I quickly found &lt;a href="http://cthedot.de/cssutils/"&gt;cssutils&lt;/a&gt;
to be a very handy CSS parser. In a matter of hours I was able to whip
up a basic dead CSS finder in 100 lines of code using these great tools.
I named it 7sense, after &lt;a href="http://www.imdb.com/title/tt0167404/"&gt;the movie&lt;/a&gt;. To run it, you just need to
invoke &lt;code&gt;./7sense.py &amp;lt;list of files and directories&amp;gt;&lt;/code&gt; and the specified
files will be parsed as if they were all part of the same webpage.
Directories will be walked recursively, with all encountered CSS and
HTML files assumed to be part of the web&amp;nbsp;page.&lt;/p&gt;

&lt;p&gt;But things are not as simple as I had hoped, and my program has several
notable&amp;nbsp;limitations.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Due to lack of time, 7sense does not look at what stylesheets are
referenced by an HTML page. Instead, you have to tell it explicitly
what stylesheets are used by passing them as&amp;nbsp;arguments.&lt;/li&gt;
&lt;li&gt;7sense does not properly decipher heirarchical CSS selectors like
'#myContainer .aboutBox'. Instead, it splits the selector into
whitespace separated tokens, ignoring their structure. I skimped on
this feature also due to lack of&amp;nbsp;time.&lt;/li&gt;
&lt;li&gt;More fundamentally, 7sense is not aware of any Javascript
modifications to the DOM. This could be remedied on a case-by-case
basis. For example, one could write a parser to seek jQuery.setClass
invocations, and extract additional classes from&amp;nbsp;there.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At any rate, here's &lt;a href="http://www.borismus.com/wp-content/uploads/2008/12/7sense.py"&gt;7sense so far&lt;/a&gt;. Though flawed, it's a useful
start. Ideas and code improvements, especially addressing the above
limitations are very much&amp;nbsp;appreciated!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/i-see-dead-css</feedburner:origLink></entry><entry><title>Musical Mashups in Pure JavaScript</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/VV5wPtfuINg/mashups-in-pure-js" /><updated>2008-12-04T21:18:37Z</updated><published>2008-12-04T21:18:37Z</published><id>http://smus.com/mashups-in-pure-js</id><content type="html">
      
&lt;h1&gt;
  Musical Mashups in Pure&amp;nbsp;JavaScript
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Dec. 4, 2008"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Dec. 4, 2008&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;When I want to learn a new song on guitar, I often search for chords
online. There are many sites that provide chords and tabs, and Google
indexes them nicely. But the quality of chords is often poor, and
there's no way to submit corrections. When I ran a MoinMoin wiki, I kept
my fixed versions of songs there. Even making modifications to existing
chords was painful though, since it involved hand-editing a plain text
file and ensuring that the chords were properly aligned with the lyrics.
My preferred solution to this problem is to write a web application to
facilitate easy collaborative editing of simple folk/rock/pop&amp;nbsp;songs. &lt;/p&gt;

&lt;p&gt;I started working on a proof-of-concept prototype. To begin with, I wanted
an easy way of finding song lyrics, which provide the skeleton for most
guitar pieces that I'm interested in. Later, I planned to work on
annotating those lyrics. As I prepared to whip up a light django
application to scrape lyrics sites, I realized that there may be a
simpler way: if I found a friendly lyrics API, there would be no need to
write any server side code. Could the entire service be written in&amp;nbsp;Javascript? &lt;/p&gt;

&lt;p&gt;LyricWiki.org had exactly what I need: a simple way to
access &lt;a href="http://lyricwiki.org/api.php?artist=Slipknot&amp;amp;song=Before_I_Forget&amp;amp;fmt=json"&gt;lyrics in JSON&lt;/a&gt; format. Of course, you can't just do an
XmlHttpRequest to lyricswiki.org because of XSS security restrictions.
Instead, you &lt;em&gt;can&lt;/em&gt; work around this cross-domain scripting issue by
writing out &lt;script\&amp;gt; tags, and specifying the script src dynamically.
Luckily, the LyricWiki JSON is wrapped (padded) in a variable named
'song'. As I later discovered, this practice of wrapping JSON in a
variable is well established, and called &lt;a href="http://en.wikipedia.org/wiki/JSONP"&gt;JSONP&lt;/a&gt;. This technique makes
it easy to simply evaluate the script tag, and wait for the song
variable to change. Now If only there was a safe way of doing this sort
of cross-domain scripting without introducing a &lt;a href="http://personalized20.blogspot.com/2006/02/jsonp-service-and-security.html"&gt;host&lt;/a&gt; of &lt;a href="http://unclehulka.com/ryan/blog/archives/2005/12/12/jsonpyoure-joking-right/"&gt;XSS&lt;/a&gt;
&lt;a href="http://www.west-wind.com/Weblog/posts/107136.aspx"&gt;vulnerabilities&lt;/a&gt;, writing mashups would be a walk in the&amp;nbsp;park! &lt;/p&gt;

&lt;p&gt;After only two hours of hacking around, including learning jQuery, I came up
with a &lt;a href="/x/lyricwiki"&gt;little demo application&lt;/a&gt;, written in pure Javascript. Hooray for
jQuery, humanmsg, and the LyricsWiki&amp;nbsp;API!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/mashups-in-pure-js</feedburner:origLink></entry><entry><title>Moving Day</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/JecwC2oqhag/moving-day" /><updated>2008-11-24T01:08:28Z</updated><published>2008-11-24T01:08:28Z</published><id>http://smus.com/moving-day</id><content type="html">
      
&lt;h1&gt;
  Moving&amp;nbsp;Day
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Nov. 24, 2008"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Nov. 24, 2008&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Fear (of &lt;a href="http://www.shaw.ca/en-ca"&gt;Shaw&lt;/a&gt; downtime) is the mind killer. So as of today, this
blog is hosted on &lt;a href="http://www.webfaction.com/"&gt;WebFaction&lt;/a&gt;.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/moving-day</feedburner:origLink></entry><entry><title>Robotic Piano Playback</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/ouXdi7KyxYI/robotic-piano-playback" /><updated>2008-11-15T02:56:39Z</updated><published>2008-11-15T02:56:39Z</published><id>http://smus.com/robotic-piano-playback</id><content type="html">
      
&lt;h1&gt;
  Robotic Piano&amp;nbsp;Playback
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Nov. 15, 2008"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Nov. 15, 2008&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;After several weeks of casual spare-time research and implementation,
I've finally built a fully working piano playback robot. The usage is
simple: someone plays or sings an arbitrary monophonic melody, and the
robot, parked on a piano bench, will play it&amp;nbsp;back.&lt;/p&gt;

&lt;iframe class="youtube-16x9" title="YouTube video player"
  src="http://www.youtube.com/embed/Bo0eCSkjy-0" frameborder="0"
  allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;The physical construction of the robot is very simple: it consists of a car
with a crane-like arm mounted on it. The arm is used to push down and release a
single piano key. On either end of the car, there are sensors which detect if
the robot has come too close to the edges of the piano bench. The simplicity of
the robot comes at the price of significant limitations, such as only being
able to play back melodies on the white&amp;nbsp;keys. &lt;/p&gt;

&lt;p&gt;Software is the challenging part of the project. The Mindstorms sound sensor is
too primitive to use for pitch analysis. Without hacking it, you can only
extract the amplitude of the sound signal, not any frequency details.  Instead
of the NXT sound sensor, I use a macbook pro and it's built-in microphone. A
computer separate from the NXT is involved, so an additional set of
communication problems&amp;nbsp;arose. &lt;/p&gt;

&lt;p&gt;Here's a rough outline of happens to make the playback work, from capturing the
melody line to playing the melody&amp;nbsp;back.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;On the mac, using a &lt;a href="http://appscript.sourceforge.net/"&gt;Python AppleScript bridge&lt;/a&gt;, the QuickTime
Player is invoked and starts capturing&amp;nbsp;audio.&lt;/li&gt;
&lt;li&gt;Once the audio is captured into an AIFF file, a very useful 
&lt;a href="http://aubio.org/"&gt;pitch detection library&lt;/a&gt; called aubio processes the audio file and
extracts raw frequency-to-time data, sampled at some tick rate.
Compiling this library on OS X was quite a&amp;nbsp;feat!&lt;/li&gt;
&lt;li&gt;Next, the raw data is processed by throwing out extraneous values
and extracting a&amp;nbsp;melody&lt;/li&gt;
&lt;li&gt;Once we have the melody line, we inject it into &lt;a href="http://bricxcc.sourceforge.net/nbc/"&gt;an NXC program&lt;/a&gt;,
and compile it with the nbc&amp;nbsp;compiler&lt;/li&gt;
&lt;li&gt;This program is then sent via bluetooth to the robot via&amp;nbsp;nxtcom&lt;/li&gt;
&lt;li&gt;Using &lt;a href="http://home.comcast.net/~dplau/nxt_python/"&gt;NXT_Python&lt;/a&gt; and &lt;a href="http://www.cs.wlu.edu/~levy/software/nxt_lightblue_glue/"&gt;Lightblue Glue&lt;/a&gt;, the robot is told to
execute the&amp;nbsp;program.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Please let me know if you have any questions or&amp;nbsp;suggestions!&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/robotic-piano-playback</feedburner:origLink></entry><entry><title>The (Sorry) State of HTML Mail</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/Q_kineGYs3w/the-sorry-state-of-html-mail" /><updated>2008-10-31T12:03:12Z</updated><published>2008-10-31T12:03:12Z</published><id>http://smus.com/the-sorry-state-of-html-mail</id><content type="html">
      
&lt;h1&gt;
  The (Sorry) State of HTML&amp;nbsp;Mail
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Oct. 31, 2008"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Oct. 31, 2008&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Web design used to be a black art. Ten years ago, browser differences used to
be so dramatic that the only viable solution for an HTML designer was to fall
back to the least common denominator for page layout, which was HTML tables. In
today's web design community, using table layouts is considered to be a heinous
crime, since most popular modern rendering engines (IE, Gecko and WebKit) are
converging to some shared interpretation of web standards. Unlike layout
engines on the web, though, rich mail interpreters have remained stagnant, and
in some cases have regressed. &lt;strike&gt;Without pointing any fingers...&lt;/strike&gt;&lt;/p&gt;

&lt;p&gt;Much of the blame for this difference lies in Microsoft's decision to use the
same engine for composing and viewing email in Outlook 2007. They wanted to
make the life of email designers easier, or so the story goes. But since
Internet Explorer doesn't have editing functionality, and Front Page is too
heavy to embed, the remaining choice was Word '07. Unfortunately, Word's
rendering engine is extremely limited, in the following notable&amp;nbsp;ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;No positioning or floating elements, so CSS-based layouts are&amp;nbsp;out&lt;/li&gt;
&lt;li&gt;No CSS backgrounds, combined with (1) means that there's no way to
have an image background at&amp;nbsp;all.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, if you care a significant population of email readers (7% use Outlook '07),
and want to deliver a rich media email, you are condemned to designing with
table layouts, or to skip HTML altogether, and simply use&amp;nbsp;images. &lt;/p&gt;

&lt;p&gt;For a long time, there was no good way of determining a
breakdown of email client usage. Recently, the good people at
fingerprint have come up with a solution, and now use a large sample of
people to determine global &lt;a href="http://fingerprintapp.com/email-client-stats"&gt;email client usage statistics&lt;/a&gt;. According
to them, Hotmail, Yahoo Mail and Gmail add up to roughly 50% of all&amp;nbsp;usage. &lt;/p&gt;

&lt;p&gt;Since the UI of a web-based email client is written in HTML, so it is critical
for webmail developers to ensure that the CSS and HTML found in the email does
not interfere with the global look and feel. The canonical solutions are to
decorate all ids and classes in the email with some kind of prefix to ensure
that there are no name collisions, and use a white list approach to CSS styles.
These white lists are usually quite long, but lack some important and oft-used
properties. For example, CSS image backgrounds are disallowed, except on Yahoo.
And of course, Microsoft had to leave its bizarre mark too: Hotmail strips the
margin-top property, but not the margin-bottom&amp;nbsp;property. &lt;/p&gt;

&lt;p&gt;Historically,
webmail clients used to ignore anything outside of the &lt;code&gt;&amp;lt;body\&amp;gt;&lt;/code&gt; tag,
which meant that all CSS had to be written inline, leading to
unmaintainable layouts. In recent tests, however, popular webmail
clients no longer ignore &lt;code&gt;&amp;lt;style\&amp;gt;&lt;/code&gt; elements in the &lt;code&gt;&amp;lt;head\&amp;gt;&lt;/code&gt;, and instead,
apply the CSS sparingly inline. Among modern native mail clients, there
is a positive trend as well. Microsoft Live Mail uses the IE7 rendering
engine, Thunderbird uses Gecko and Mail.app uses WebKit. So it looks
like there is light at the end of the tunnel for downtrodden HTML email
designers. In the meantime though, I send them my deepest&amp;nbsp;condolences.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/the-sorry-state-of-html-mail</feedburner:origLink></entry><entry><title>Top 5 Sessions of Web 2.0 Expo NY</title><author><name>Boris Smus</name></author><link href="http://feedproxy.google.com/~r/smuscom/~3/aUtvhz1gUk0/top-5-sessions-of-web-2-expo-ny" /><updated>2008-09-23T00:22:16Z</updated><published>2008-09-23T00:22:16Z</published><id>http://smus.com/top-5-sessions-of-web-2-expo-ny</id><content type="html">
      
&lt;h1&gt;
  Top 5 Sessions of Web 2.0 Expo&amp;nbsp;NY
&lt;/h1&gt;

&lt;div class="date"&gt;
  Posted &lt;span class="relative" title="Sept. 23, 2008"&gt;&lt;/span&gt; on
  &lt;span class="absolute"&gt;Sept. 23, 2008&lt;/span&gt;
  &lt;g:plusone size="small"&gt;&lt;/g:plusone&gt;
&lt;/div&gt;

&lt;p&gt;Yesterday I got back from the Web 2.0 Expo in New York. There were many
interesting sessions, and I, for the most part, took notes! Here is a
short list of my five favorite speakers, in no particular&amp;nbsp;order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cal Henderson's whirlwind &lt;a href="http://wiki.z3.ca/Web%202.0%20Expo/Moving%20Pictures"&gt;introduction to video on the web&lt;/a&gt;. Cal
really likes speak really fast, to make typos in his slides and to
cuss, all of which makes for a very informative and entertaining&amp;nbsp;hour.&lt;/li&gt;
&lt;li&gt;John Resig's &lt;a href="http://wiki.z3.ca/Web%202.0%20Expo/Processing.js"&gt;talk about Processing.js&lt;/a&gt;, his port of
&lt;a href="http://processing.org/"&gt;Processing&lt;/a&gt; to canvas. I'm constantly dealing with graphics on
the web, and it's awesome to have such a powerful library available.
Pretty demos,&amp;nbsp;too!&lt;/li&gt;
&lt;li&gt;Jason Fried's short but sweet keynote talk, focusing on minimalism
in product management, and proper (read: very narrow) scoping of
features. The philosophical question of "what would your software be
like if it was physical?" struck me as a very useful thing to think&amp;nbsp;about.&lt;/li&gt;
&lt;li&gt;A &lt;a href="http://wiki.z3.ca/Web%202.0%20Expo/Browser%20Panel"&gt;browser panel&lt;/a&gt; including Chris Wilson from MS, Brendan Eich
from Mozilla and an unnamed developer from Chrome responded to a
nice set of questions about future directions of browsers. Poor
Chris got a beating IE7's flaws, lack of canvas/SVG support,
barriers to plugin development for&amp;nbsp;IE.&lt;/li&gt;
&lt;li&gt;Geir Magnusson Jr delivered an excellent introduction to &lt;a href="http://wiki.z3.ca/Web%202.0%20Expo/Sequel%20to%20SQL"&gt;scaling data in the cloud&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I also had the chance to talk to and bounce some ideas off of Pete
Koomen, a Google App Engine product manager. He told me that there are
plans both for process scheduling, and for support of django-1.0 down
the road, but of course gave no time frame for either. Overall, the
conference was interesting - there were other good sessions and keynotes
which I simply haven't bothered to write up. The crowd wasn't very
technical though, comprising in large part designers, marketers and&amp;nbsp;managers.&lt;/p&gt;



    </content><feedburner:origLink>http://smus.com/top-5-sessions-of-web-2-expo-ny</feedburner:origLink></entry></feed>

