diff --git a/README.md b/README.md
index aa38c0db9f9b91ff99c37faf6f60f724822a06fc..c81a45db7e817eea091c85c6f36506adbc9e8e3d 100644
--- a/README.md
+++ b/README.md
@@ -74,7 +74,7 @@ See [jest cli docs](https://facebook.github.io/jest/docs/en/cli.html#content) fo
 
 *NOTE: If you're a windows user, please run these scripts in Windows `cmd.exe`  instead of Git Bash/MINGW64.*
 
-Before running any test, make sure you have run `npm install` from this (scratch-gui) repository's top level.
+Before running any tests, make sure you have run `npm install` from this (scratch-gui) repository's top level.
 
 #### Main testing command
 
@@ -194,5 +194,60 @@ Further reading: [Stack Overflow](https://stackoverflow.com/questions/46602286/n
 You can publish the GUI to github.io so that others on the Internet can view it.
 [Read the wiki for a step-by-step guide.](https://github.com/LLK/scratch-gui/wiki/Publishing-to-GitHub-Pages)
 
+## Understanding the project state machine
+
+Since so much code throughout scratch-gui depends on the state of the project, which goes through many different phases of loading, displaying and saving, we created a "finite state machine" to make it clear which state it is in at any moment. This is contained in the file src/reducers/project-state.js .
+
+It can be hard to understand the code in src/reducers/project-state.js . There are several types of data and functions used, which relate to each other:
+
+### Loading states
+
+These include state constant strings like:
+
+* `NOT_LOADED` (the default state),
+* `ERROR`,
+* `FETCHING_WITH_ID`,
+* `LOADING_VM_WITH_ID`,
+* `REMIXING`,
+* `SHOWING_WITH_ID`,
+* `SHOWING_WITHOUT_ID`,
+* etc.
+
+### Transitions
+
+These are names for the action which causes a state change. Some examples are:
+
+* `START_FETCHING_NEW`,
+* `DONE_FETCHING_WITH_ID`,
+* `DONE_LOADING_VM_WITH_ID`,
+* `SET_PROJECT_ID`,
+* `START_AUTO_UPDATING`,
+
+### How transitions relate to loading states
+
+As this diagram of the project state machine shows, various transition actions can move us from one loading state to another:
+
+![Project state diagram](docs/project_state_diagram.svg)
+
+_Note: for clarity, the diagram above excludes states and transitions relating to error handling._
+
+#### Example
+
+Here's an example of how states transition.
+
+Suppose a user clicks on a project, and the page starts to load with url https://scratch.mit.edu/projects/123456 .
+
+Here's what will happen in the project state machine:
+
+![Project state example](docs/project_state_example.png)
+
+1. When the app first mounts, the project state is `NOT_LOADED`.
+2. The `SET_PROJECT_ID` redux action is dispatched (from src/lib/project-fetcher-hoc.jsx), with `projectId` set to `123456`. This transitions the state from `NOT_LOADED` to `FETCHING_WITH_ID`.
+3. The `FETCHING_WITH_ID` state. In src/lib/project-fetcher-hoc.jsx, the `projectId` value `123456` is used to request the data for that project from the server.
+4. When the server responds with the data, src/lib/project-fetcher-hoc.jsx dispatches the `DONE_FETCHING_WITH_ID` action, with `projectData` set. This transitions the state from `FETCHING_WITH_ID` to `LOADING_VM_WITH_ID`.
+5. The `LOADING_VM_WITH_ID` state. In src/lib/vm-manager-hoc.jsx, we load the `projectData` into Scratch's virtual machine ("the vm").
+6. When loading is done, src/lib/vm-manager-hoc.jsx dispatches the `DONE_LOADING_VM_WITH_ID` action. This transitions the state from `LOADING_VM_WITH_ID` to `SHOWING_WITH_ID`
+7. The `SHOWING_WITH_ID` state. Now the project appears normally and is playable and editable.
+
 ## Donate
 We provide [Scratch](https://scratch.mit.edu) free of charge, and want to keep it that way! Please consider making a [donation](https://secure.donationpay.org/scratchfoundation/) to support our continued engineering, design, community, and resource development efforts. Donations of any size are appreciated. Thank you!
diff --git a/docs/project_state_diagram.svg b/docs/project_state_diagram.svg
new file mode 100644
index 0000000000000000000000000000000000000000..e24382e5a0b4398bc1c17d9d719a05f50c09ffb5
Binary files /dev/null and b/docs/project_state_diagram.svg differ
diff --git a/docs/project_state_example.png b/docs/project_state_example.png
new file mode 100644
index 0000000000000000000000000000000000000000..920c3087db24c967863d30e19e7fb77d9d8f6d33
Binary files /dev/null and b/docs/project_state_example.png differ