Shed is a collaborative editor I patched together earlier this year for working on problems with algorithms students. Shed is built using ot.js, codemirror and docker - multiple people can write code in the same paste and then run it. It’s been working great (minus a few small hiccups) and now I use it with most of the people I work with.
Within a few weeks of using it, one of the students suggested a really cool feature: the ability to watch a replay of a problem solving session. So of course, I went and implemented that feature and it’s been working surprisingly well. After every session, students can now look back at how the session went and get a sense of their timing while solving problems.
For the first draft of replays, I had the server store each operation from OT.js into sqlite. When loading a replay, all the operations would be sent to the client and then replayed through OT.js by simulating the original OT.js events.
This worked well as a proof of concept, but there were a few problems:
- a half hour session took about 500K of data to save a replay
- the performance of large sessions started becoming untenable: loading a half hour session could take multiple seconds
- jumping to a particular point in history was laggy
Storing the data as gzip blobs was able to effectively reduce the size required for replays: while a session is ongoing, the server stores each operation in a table and the session is later compacted into a single row in a Blob table and then gzipped.
Now the session size was reasonable but performance was still terrible: trying to fast forward or jump to any point in history was laggy and difficult. After profiling the page, I realized that CodeMirror was doing work on every change: re-adjusting the syntax highlighting and reflowing and re-drawing the DOM. This is a big no-no: we want to avoid touching the DOM as much as possible.
To reduce the amount of work on the client, I ended up creating a client for OT.js that acts similarly to the server side OT client - it tracks a document without changing the DOM. I then replay all the operations and create a history of the document over time. When the slider is dragged, shed picks the document at that particular point in history and sets the content of CodeMirror to its value. The redraw is throttled to every 200ms to reduce the amount of DOM redraws.
The source for shed is available on github and the replay feature is live and ready to play with.