ToyBoat Raft (code)

Summer 2015


emacs-asana (code)

Spring 2017

functional but PRs welcome

Pinboard Viewer (code)

Summer 2015


Gazebo (code)

Summer 2014


Axis&Allies (code)

Summer 2013


TVTracker (code)

Spring 2013


2EZ (Url Shortener) (code)

Spring 2014


EasyAuth (code)

Winter 2013


RTFA (code)

Winter 2013


PopOut (code)

Spring 2014


ToyBoat - A Raft implementation

ToyBoat Raft demo

Raft is a distributed consensus algorithm. Consensus algorithms provide strong consistency guarantees to a cluster of servers, coordinating agreement on a value or safely replicating a state machine. Raft is mostly-equivalent to an earlier algorithm called Paxos, but was designed specifically to be easier to understand and leave less as an "exercise for the reader." You can read the original paper for an excellent description of the algorithm and its many uses. You can also read any Paxos paper to see why an easier and more exaustively-specified algorithm was necessary.

This repo contains my own implementation of Raft, in Haskell. I've implemented the full protocol, the two big pieces being log-entry propagation and leader election. Performance was not my focus, but it's fast enough to be IO-bound (and constrained by the configurable heartbeat/election timers) rather than CPU-bound. I also skipped space optimizations like log compaction, since I'm not using this in production anywhere and the log files take a long time to get huge.

Quick Start

Installing haskell is a huge pain for no reason, so if you don't have it I'd recommend just downloading the repo with precompiled binaries from the GitHub release page. If you just want to run the whole thing and see what's going on, here's how to do that:

# run the default cluster in three terminal tabs.
# when a prompt pops up in one of them, type numbers in to submit log entries.
./raft-osx 1
./raft-osx 2
./raft-osx 3
# to see what's going on under the hood, open 3 more terminal tabs:
tail -100f log/debug.1.log
tail -100f log/debug.2.log
tail -100f log/debug.3.log

It'll look like the picture above.

Running ToyBoat

Here's the full usage information:

./raft-osx SERVER_ID [COMMAND]   # commands = [log, test, leader, candidate, follower]
./raft-linux SERVER_ID [COMMAND] # commands = [log, test, leader, candidate, follower]

./raft-osx 1               # runs a Raft instance with ID=1
./raft-osx 3 candidate     # runs a Raft instance with ID=3, forcing it into a particular starting role (candidate)
./raft-osx 2 log           # dumps a nicely-formatted version of Server 2's write-ahead log, found in db/log.ID.json.
./raft-osx 1 test          # runs the tests for Server 1. The test harness has been set up, but there aren't many tests.

The config file, conf/config.json, sets up a 3-node cluster to run locally. You can manually edit the JSON to change this, if you like.

Compiling ToyBoat

ToyBoat compiles and runs on OSX and Linux. The repo includes a Rakefile for simplifying the compilation commands as well as managing Docker if you're on OSX but want the Linux version.

You need Ruby and Rake to build the project. Maybe someday I'll learn to enjoy writing Makefiles, but for now, google installing RVM on [MY OS].

You need Haskell too (obviously). Installing it is hard but Google-able. The project uses GHC version 7.10. Once you have Haskell, cabal build will install the project's dependencies.

Finally, you're ready to actually compile the damn project. Assuming you're running OSX rake compile will compile for OSX, and rake docker:build; rake docker:compile will compile for Linux using Docker.

Using the Rakefile

Full list of Rakefile commands, for convenience:

rake                # default: compile for both OSX and Linux
rake reset          # reset the state machine, clear the write-ahead logs
rake log[ID]        # dump the server log for server #ID; alias for ./raft-OS ID log
rake debug[ID]      # tail the log file for server #ID
rake compile        # compile for OSX
rake run[ID]        # run server #ID; alias for ./raft-OS ID

rake docker:build   # build a linux image with Haskell and Raft's dependencies
rake docker:compile # compile for Linux using docker
rake docker:run[ID] # run server #ID on Linux inside docker

if you're messing around with Docker you'll probably have to edit the Rakefile; there are a few Docker commands that insist on absolute paths.


Browse and act on your My Tasks list without leaving Emacs!



You'll need these packages, available from melpa via M-x package-install: helm exec-path-from-shell

Getting Started

Add this to your .bash_profile (or wherever you keep your environment):

# Get a Personal Access Token from the `apps' tab in your profile settings.
export ASANA_TOKEN="<my-asana-personal-access-token>" 

Add this to your init.el: elisp (global-asana-mode 1) ;; or, if you prefer, use it in specific major modes: ;; (add-hook 'org-mode-hook 'asana-mode) ;; (add-hook 'prog-mode-hook 'asana-mode) ;; (add-hook 'text-mode-hook 'asana-mode)


[Optional] The default prefix for asana-mode commands is C-c a. To change it, add this to your init.el: elisp (setq asana-keymap-prefix "C-c C-a") ; Or whatever you'd like to use as your prefix

Available commands

In the asana-mode minor mode, the following interactive commands are available:

helm-asana [C-c a a]
helm-asana-change-workspace [C-c a A]

asana-create-task-quickly [C-c a c]
asana-create-task [C-c a C]

The helm-asana task list provides these actions: Select (view task details in buffer) [RET] Browse (open in Asana) [C-b] Move to section [C-:] Complete [C-RET] Delete [C-DEL] Move all marked tasks to section [M-:] Complete all marked tasks [M-RET] Delete all marked tasks [M-DEL]

Known issues

OSX El Capitan can break exec-path-from-shell, which corrupts your access token as it gets imported into Emacs. See for more details. One available workaround is to disable OSX bash sessions with touch ~/.bash_sessions_disable.


  • Pagination for > 100 tasks
  • Improve API request batching & async around multi-select and assignee_status updates
  • Improve error messages, for example around ASANA_TOKEN not found
  • Improve/add docstrings
  • Publish v1.0 as package

Pinboard Viewer

Make Pinboard ( pins act more like browser bookmarks. Includes fuzzy search by pin title and url. Click the button or use a keyboard shortcut (suggested: Cmd-Shift-P or Ctrl-Shift-P) to bring up your Pinboard bookmarks. Search for tag names to filter, or search for a page title or URL to find a specific bookmark.

The results update as you type. Once you've found your bookmark, press Enter or Tab to open it. Hold Cmd (Mac) or Ctrl (Windows) to open it in a new tab.

Click here for screenshots and a download link.


I built this while putting off choosing classes. It helps you choose classes.

How to use

  • Click the red link to change between calendar and requirements modes
  • Click and drag to move courses between requirements or semesters
  • Type in the box to search for and add new courses
  • Click a requirement name or TODO box to see a list of options
  • Right click a course to get its description + offered terms


Axis & Allies is a tabletop minatures game I played a lot as a kid. This is my HTML5 tribute to that game: it's animated, multiplayer, completely free, and (I think) a lot of fun, so give it a try! This is probably my largest solo project.

Tech stuff: - Pure javascript (both client and server) - Multiplayer over the web, with both synchronous and asynchronous gameplay - Heavily database-driven: features autosave and instant replays - Infrastructure built with Meteor.js, an awesome (and as of writing half-finished) web framework


  • Draft Phase: Click a unit's name to see its card. Choose your units using the number boxes in the cards.
  • Deploy Phase: Click on the board to deploy a unit; click again to take it back.
  • Movement and Assault Phases: Click a friendly unit to select it, then click an empty hex to move there. Or, right-click an enemy unit to inspect it.
  • Assault Phase: with a unit selected, enemies within attack range will be highlighted. Click an enemy unit to attack.

How to Play

Axis and Allies is a turn-based strategy game for two players. The game is played in rounds, and each round has several phases. At the start of each round, one player is chosen randomly to play first during each phase of that round.

Setup Round

  • Draft Phase: Build an army.
    • Each unit has a cost, and you have 100 points to spend.
  • Deployment Phase: Place your troops.
    • If you are the first player to deploy, you can choose to deploy in the East or West.
    • Each unit must be placed within 3 hexes of your border.

Play Rounds

  • Movement Phase: Move your units.
    • Each unit can move once during your movement phase.
    • Vehicles are slower moving through Forests and Hills, and cannot move through Marsh.
    • No units can move into Ponds.
  • Assault Phase: Attack or move again.
    • Each unit can either attack or move (but not both) during your assault phase.
    • The Attacks table on each unit's card shows the number of attack dice it gets against each unit type. The number of attacks decreases when you attack from farther away.
    • An attack succeeds on a roll of 4+. The number of successes determines the attack's effect:
      • Less than enemy's defense: you missed.
      • Equal to enemy's defense: one hit.
      • Greater than enemy's defense: two hits.
      • Double the enemy's defense: three hits.
    • Hits from multiple attackers are added together.
  • Casualty Phase: Hits from assault phase take effect.
    • 1 hits: Soldiers and vehicles are disrupted. Disruption lasts for 1 turn, preventing movement and weakening attacks (-1 to each die)
    • 2 hits: Soldiers and damaged vehicles are destroyed; undamaged vehicles take damage. Damage is a permanent -1 to movement range and attack rolls.
    • 3 hits: Undamaged vehicles are destroyed. Destroyed units are removed from play.
    • Units in defensive terrain (all units: Towns, Forests, Hills; soldiers only: Marsh, Shell Holes) can take cover, reducing multiple hits to 1.
    • Soldiers have a 50% chance of taking cover. Vehicles have a 33% chance of taking cover.


  • Once 7 rounds have passed, you can win by controlling the objective. If only you have units on or immediately next to to the objective (the crosshairs), you win!
  • Once 10 rounds have passed, you can also win by military superiority. If you have more points' worth of units still in play than your opponent, you win!


My first major, finished(ish) personal project. Pumped!


  • Create an account
  • Start tracking existing TV shows--or add your own!
  • Read plot summaries and actor lists
  • Keep track of which episodes you've watched, and when the next one's airing
  • Enjoy some snazzy jQuery

Why I made it

I got to play around with APIs, somewhat-complex jQuery, and deal with building a full rails app from start to finish.

I also actually use this to keep track of all the TV shows I [never have time to] watch --and I hope that someone else will too. That'd be exciting.

How it works

TVTracker uses two APIs to gather the information it needs: * TVRage for the episode lists * OMDB for the posters and show-level information like plot summaries

Url Shortener

A simple link shortener hosted at my personal website. This was a fun little project, inspired by my realization that is the same length as

The shortening method

I wanted the links to be very short but still look random, so they're not really very random. Links are 3 characters, and must include at least one letter and at least one number. They're case insensitive, so you can read them over the phone or something.


  • Private links, track with either email auth or password
  • Track request referrers


A little daemon that uses Twilio and Mailgun to intercept SMS messages and forward them to other phone numbers and email addresses. Use it to make Stanford 2Factor authentication less of a hassle!

Tech Stuff

  • Ruby with Sinatra
  • Twilio API (10/10, would API again)


  • Integrate Google Authenticator or similar. This would shave a second or two off the code generation time.


(Read the effing article! Form your own opinions!)

A simple chrome extension that hides comment threads on news aggregator sites (currently HackerNews and Reddit) until after you've read the article.


On these sites I have an unfortunate habit of going to the comments to get the "gist" and then skipping the article entirely. I'm trying to break this habit... and spend less time in comment sections in general.

And I wanted to try building a chrome extension. (It was fun. B+!)


As it turns out, for security reasons you can't actually use js to determine whether a user has visited a link. So this extension manually keeps track of your last few hundred pages visited from HN/Reddit. In practice this works well enough.




Get it from the Google webstore.


A tiny chrome extension that adds a 'Pop out tabs to right' button to your menu bar. Click it to move your current tab and all tabs to the right to a new browser window.

Feel free to contact me with any questions!

Install PopOut

Get it from the Google webstore.