For last week’s post click here.

This week, I worked on a recording system for Pumpkin Man’s control interface. This enables us to prerecord a sequence of motions and play it back later.

Creating a file format for recordings

I decided to structure my data as a sequence of timestamps and associated actions, something like this:

 time |    action
------|---------------
    0 | open mouth
  100 | close mouth
  600 | open mouth, raise arm
 1500 | close mouth
 2000 | lower arm
----------------------

This format makes sense for this type of recording because it doesn’t waste space on periods of time where nothing changes, and it doesn’t limit the number of changes that can take place at any particular timestamp.

I ended up representing this data using JSON because it was easy to do so. I also added in a byte to represent the file version, in case I need to change the format but want to still support playing back older versions. I spent a little bit of time thinking about creating a custom binary format that would use space more efficiently than JSON1, but quite quickly I realized I was wasting my time on a non-issue. These recordings won’t be very large regardless.

Recording and playing back

Creating a recording and playing it back, on the server side, isn’t all that hard. Creating a recording just requires that you log every change that the server sees along with their timestamps relative to the start of the recording (I used timestamps in integer milliseconds for my purposes). Playback involves running through the recording line-by-line and waiting until enough time has passed since “play” was clicked before performing the corresponding action.

Both the recording and playback processes introduce some imprecisions in timing. When recording, there might be some latency in how long it takes the server to receive instructions and/or process them, which would then be reflected in the recording. When playing back, the “wait until enough time has passed” step has the potential to wait too long and end up being late to perform the corresponding action. My strategy for these waits is currently just to repeatedly wait in increments of 1/30th of a second and then check if it’s the right time. I could be more sophisicated in deciding how long to wait in these circumstances, especially considering I know exactly when the next event is supposed to take place. The motion Pumpkin Man will do doesn’t need to be highly precise, however, so this is fine. I can always adjust it later if it turns out to be an issue.

The rest of the team

This week, Sam and Kai made progress on Pumpkin Man’s head (he has a mouth now!) and constructed a spooky ribcage out of PVC (which will be dressed up with foliage or vines or something once it has “skin”). Pumpkin Man is huge and I’m so excited to see his final form!


The code is still available on GitHub, and just like last week I’d like to disclaim that code quality is not my focus in this project as much as quick iteration and feature creation. šŸ˜‰


  1. For example, in a JSON file, representing an integer takes one byte per digit in the base-10 representation of the number (because numbers are encoded as their literal character representations, each of which takes a byte). On the other hand, if I created a custom format, I could represent numbers up to 2^32 using just four bytes each, by putting their binary representations directly in the file. ↩︎