Update (November 2021)

I am solving a two-body problem in the Bay Area. Here's my CV. Also check out:


5 June 2013

You have a irregularly-timed stimulus which requires a timely, discrete response from a regular set of choices; you'd automate your job, but the data involved are a little too complex, and you have neither the time nor wherewithal to use machine learning to work your way around it. Have no fear: Reaction is here!

Reaction connects your Android device to any data source that you provide. It regularly polls your source for updates, providing helpful blinking-light notifications when you have a new task to complete. Although this is nothing that an email notification system cannot handle, Reaction goes the extra mile and allows you to dynamically respond to the notifications, taking whatever actions you find appropriate.

As a pertinent example — and the genesis for the app — consider the case of blogspam. Inasmuch as the CAPTCHAs on this site aren't too difficult for a machine to solve (there have been complaints from humans), comment spam was inundating this very website. To the tune of dozens (this very website isn't that big of a deal) of comments per day, more or less of the form:

yfymgyiqtidutd jdzvnfhbvgjyln nike australia zbfaxtgomhrbmr iwqojjcizbpkdf ray ban wayfarer zyajjkljbffflg eajlignhcssjyn nike australia qgqabmraxkewjz ypxrqhtijgysxu nike shox ormwxookcefxqw cxfykvytcemnnm vanessa bruno qdvynrjmxiimxu usxvpbnzbvkhcu air max 1

After trying to keep up a decent list of filter keywords, this was witnessed to be a losing battle. With Reaction, blog comments are automatically sent to my phone (technically my phone automatically polls for new comments), where I have the option to accept or reject their content; the rejection rate is nearly 99%.

how to make it work

Reaction uses the following data concepts:

You may set up an arbitrary number of targets. A given target polls its URL with user-specified frequency, and returns JSON-formatted data with the following structure:

There is no need for the actions of items to relate to one another; although in the blogspam context it is natural that all items should have the actions accept and reject, other uses may not satisfy this consistency requirement. Reaction does not update iteratively, and the entire list of current items must be sent during each poll. As a concrete example, the JSON response might look like the following:

{ "items": [
    "id": "0x001",
    "content": "Left or right?",
    "actions": [
        "label": "left",
        "url": "http://ex.com/rct/?id=0x001&action=left"
        "label": "left",
        "url": "http://ex.com/rct/?id=0x001&action=right"
    "id": "0x002",
    "content": "Up or down?",
    "actions": [
        "label": "up",
        "url": "http://ex.com/rct/?id=0x002&action=up"
        "label": "down",
        "url": "http://ex.com/rct/?id=0x002&action=down"
] }

I believe I have written decent in-app documentation, which is available by clicking the ? icon.

setting up a target

To see the simplicity of setting up the remote (phone) side of the application, the following instructions step you through the process of connecting to a sample dataset. This dataset automatically generates new items randomly every ten or so refreshes, so if you connect and see no items, refresh repeatedly until something comes up.

Click your phone's back button, and you're done. Easy.

For what it's worth, it would be very possible to use this system to create a multi-player text-based game; an extremely sweded one, but one nonetheless. Due to time constraints, the sample dataset is somewhat less interesting.


Reaction requires you to have set up a server with which it can communicate; if you already have a dynamic website this is a non-issue, and amounts to adding a new script (or two) to your site. To keep things unquestionably simple, I use a single, PHP-authenticated URL to handle all requests; in particular, actions return a refreshed dataset. In comment-majority pseudocode,


  // Check $_POST or $_GET parameters for acceptable username and password
  // (Reaction does not support cookies or sessions)

  // Connect to MySQL

  if ( isset( $_GET['action'] ) )
    $action = $_GET['action'];
    if ( $action == 'accept' )
      // use URL parameters to de-flag comment for moderation
      // use URL parameters to delete comment

  header( 'Content-type: application/json' );

  // Run query to obtain all comments
  // print '{ "items": ['
  // for each comment {
  //   print '{ "id": ' . $comment_id . ', ';
  //   print '"content": "' . str_replace( $json_unfriendly, $json_friendly, $comment_content ) . '", ';
  //   print '"actions": [ ';
  //   print '{ "label": "accept", "url": "http://1.618034.com/RXNREALURL.php?action=accept&id=' . $comment_id . '" },';
  //   print '{ "label": "reject", "url": "http://1.618034.com/RXNREALURL.php?action=reject&id=' . $comment_id . '" } ] }';
  // }
  // print '] }';

Clearly this is not production code, but it gets the idea across.

Reaction automatically appends authentication information to all action URLs (via GET or POST as indicated), so there is no need to specify this in the returned data. Note that since Reaction is decentralized and polls for updates, it is very possible that the same item will result in multiple actions being taken; if multiple devices are hooked to the same target, one may take an action, then another before the data is refreshed. Because of this, the data source code should take into account that this might happen, and protect against failures which may result from multiple actions. With regard to blog comments, this isn't particularly an issue.

that's all

Although setting up the server end is a chore, Reaction is at its heart a very simple application. I welcome your usability suggestions and feedback in the comments below; I'm guaranteed to see it (solid plug!). In particular, if there is anything worth improving about these instructions — which are, admittedly, free-form — tell me and we'll see what we can fix.

Included \(\LaTeX\) graphics are generated at LaTeX to png or by MathJax.

contemporary entries


there are no comments on this post