States Machine is an app to memorize the names and locations of the fifty US states using spaced repetition.

It asks two kinds of questions:


Where is

This is

File format and spaced repetition

States Machine uses SQLite as its file format, and the SuperMemo 2 algorithm to plan repetitions.

The database contains 100 learning tasks, representing name and location for each state.

For each task, it stores

(ef and reps are parameters from the SM2 algorithm)

We open a connection to the database at startup, and manipulate it as the program runs.

For example, picking the next item to test is as simple as

SELECT type, item, ef, reps FROM sm2
    WHERE next IS NULL OR next <= strftime('%s', 'now')
    LIMIT 1

After each question is answered, the app asks us to rate its difficulty:

This is right

This is used to update its parameters and calculate when it should be tested next.

One cool side effect of using SQLite as the file format: you can also explore the database manually!

Remember, the ef term represents how hard an item is to learn, where lower values are scheduled more often. We can use the database to check the hardest state:

sqlite> select type, item, ef from sm2 order by ef limit 10;

The type column is 1 for "click on the state with the given name" and 2 for "input the name of the highlighted state". This means that the hardest state (for me) is remembering the location of Wyoming.


The UI is entirely hand-rolled, and very application-specific.

It's drawn in three passes:

The map framebuffer renders each state in a different color:

pick buffer

This is used for both edge detection and to figure out which state is under the mouse (by reading back the framebuffer pixel).

Text is rendered using stb_truetype.h, from a copy of Inconsolata that's bundled in the executable.


The state shapes are based on genuine US Census Bureau shapefiles.

I implemented a slow, naive ear-clipping algorithm to triangulate them:


Speed doesn't matter here because the script is only run once, generating a C source file.

Size and speed

The full application is tiny, at about 2 MB unzipped and 1 MB zipped:

Executable size

Everything is statically compiled into the executable, so it can be deployed without building an installer.

It is also fast, opening in under 600 milliseconds on a 2013 Macbook Pro:

Executable speed

Of course, interactions have no perceptible lag or loading time. Performance is a feature: if this is a program that folks use once per day, it has to be frictionless.


The code lives on GitHub.