<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://nickivance.com/writing/feed.xml</id>
  <title>Nicki Vance: Writings</title>
  <updated>2026-09-16T00:00:00Z</updated>
  <link rel="self" href="https://nickivance.com/writing/feed.xml" type="application/atom+xml"/>
  <link rel="alternate" href="https://nickivance.com/writing/index.html" type="text/html"/>
  <author>
    <name>Nicki Vance</name>
    <uri>https://nickivance.com/</uri>
  </author>
  <entry>
    <id>https://nickivance.com/writing/20260916_sewing_vest/</id>
    <title>Sewing: Reversible Vest</title>
    <published>2026-09-16T00:00:00Z</published>
    <updated>2026-09-16T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20260916_sewing_vest/" type="text/html"/>
    <content type="html">&lt;h1 id="sewing-reversible-vest"&gt;Sewing: Reversible Vest&lt;/h1&gt;

&lt;p&gt;The reversible vest turned out great, despite some bumps in the road. I’m particularly happy about the mint green bias binding on the edges. In fact, I wish I’d also added it to the open edge of the pockets, or perhaps the whole outline of the pockets.&lt;/p&gt;

&lt;p&gt;Sometimes I make things because I want a certain thing. But sometimes, this will probably resonate with other sewers, I have a fabric I like and I want to make something with it. The light grey knit with a darker grey pattern was a gift from a friend about 4 years ago when I was starting to sew again. This is one of those patterns that is almost more of a texture. This knit is probably part cotton, part polyester and is also very thinly quilted, again probably with polyester filling. The opposite material is a black denim.&lt;/p&gt;

&lt;div class="img-grid img-grid-2"&gt;
&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20260916_sewing_vest/IMG_6057.jpg" alt=""&gt;

&lt;/figure&gt;
&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20260916_sewing_vest/IMG_6052.jpg" alt=""&gt;

&lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;Prior to this, I’ve made three sweatshirts and one collared shirt. I feel pretty comfortable with putting the vest part together but the collar can still be a little tricky.&lt;/p&gt;

&lt;p&gt;All in all, this probably took 1-2 weeks but the work was spread out over 9 months. (TBH, I lost a lot of motivation during the heat of summer. Who wants a vest in summer?)&lt;/p&gt;

&lt;h3 id="the-pattern"&gt;The Pattern&lt;/h3&gt;

&lt;p&gt;I made a pattern by tracing an existing vest and adjusting it to fit better (it was too small). I used a couple other jackets I like to check measurements for shoulder width (also a style choice for these to sit in a bit from my actual shoulder), chest width, hip width, and length.&lt;/p&gt;

&lt;p&gt;There are 4 pattern pieces: front half, back half, collar, and pocket.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20260916_sewing_vest/IMG_4120.jpg" alt=""&gt;

&lt;/figure&gt;

&lt;h3 id="colors"&gt;Colors&lt;/h3&gt;

&lt;p&gt;I experimented with colors a bit. I wanted a highlight of color. I have a variety of zippers of different colors - I love a contrast zipper. Reddish colors appealed to me but I went with the mint.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20260916_sewing_vest/IMG_4124.jpg" alt=""&gt;

&lt;/figure&gt;

&lt;h3 id="edge-binding"&gt;Edge Binding&lt;/h3&gt;
&lt;p&gt;I opted to bind the edges instead of hiding seams on the interior of the vest. This gave me just the right amount of color highlight across the whole vest. I’m a fan of contrast edgebinding for a lot of clothing.&lt;/p&gt;

&lt;p&gt;I tried out black denim first, thinking that I would use each of the primary fabrics as the binding on the opposite side. But then I realized that the effect would only work for the pockets because well, both sides share the same edge along the arms, collar, and bottom edge.&lt;/p&gt;

&lt;p&gt;It was super handy to mock this up on an extra piece of material to see how difficult it was to edge bind with denim (quite) and how it looked over the knit material.&lt;/p&gt;

&lt;p&gt;Binding an edge, though, takes some practice. I’m getting better but there are always places where I go a little close to the edge and get a visible thread on the other side. On the light side, it doesn’t show as much but the mint thread is higher contrast on the dark side.&lt;/p&gt;

&lt;p&gt;In this photo, you can see an earlier test of a black binding and a small mistake where the thread crosses outside the binding. (Isn’t the mint binding much nicer??)&lt;/p&gt;
&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20260916_sewing_vest/IMG_4119.jpg" alt=""&gt;

&lt;/figure&gt;

&lt;h3 id="pockets"&gt;Pockets&lt;/h3&gt;
&lt;p&gt;The most important feature of any clothing item: pockets.&lt;/p&gt;

&lt;p&gt;I needed to iterate on the size here because my technique for hemming the edges took up more material than expected and made the pocket a bit too small.&lt;/p&gt;

&lt;p&gt;These work pretty well but somehow the alignment of the pockets between the sides is off - the pockets on the light side are a little lower than the dark side - and as a result the light side is a little more comfortable. Maybe I mixed up the measurements.&lt;/p&gt;

&lt;p&gt;Here are the two different pocket sizes:&lt;/p&gt;
&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20260916_sewing_vest/IMG_5136.jpg" alt=""&gt;

&lt;/figure&gt;

&lt;h3 id="closures"&gt;Closures&lt;/h3&gt;

&lt;p&gt;You know, the zipper color options are pretty limited for double sided zippers. I looked for a long time for a mint green zipper with mint green tape. They exist, but only as one-sided zippers.&lt;/p&gt;

&lt;p&gt;I thought, ok, the important part of a double sided zipper is that the zipper pull has a loop that allows the pull to slide over to the opposite side of the zipper head. Maybe I can just replace the zipper head. &lt;strong&gt;It is definitely hard to find a mint green reversible zipper head.&lt;/strong&gt; Maybe paint it? I don’t really want to go through all the iterations of trying to find the right color paint and to learn how to paint a tiny thing like that well. Maybe 3D print it? No, no, wow, I don’t have a printer that can do detail that fine, even if it could be sturdy enough to be pushed and pulled over hard plastic teeth. That sounds like another deep side project.&lt;/p&gt;

&lt;p&gt;Other options for closures: buttons, loops &amp;amp; toggles, string ties, snaps.&lt;/p&gt;

&lt;p&gt;Snaps seemed fun and a good match for the style. However, it took me a good hour plus to wrap my head around how they might work for a reversible garment. I tested out the snaps to make sure I understood which sides went together. Then I had to make a paper mockup and label the sides A, B, C, D, and fold it around different ways to make sure that B-C and A-D matched up in either direction. Got in some good shape rotator practice there.&lt;/p&gt;

&lt;p&gt;Once I trusted that I could assemble the correct parts for each snap, it was an easier process than I expected. So far though, the downside is that they rattle a tiny bit which implies that I didn’t smash them with the mallet enough. I’m also a bit scared that I will rip them out of the cloth trying to open one - I’ll report back if that happens.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20260916_sewing_vest/IMG_6047.jpg" alt=""&gt;

&lt;/figure&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20260916_sewing_vest/IMG_6048.jpg" alt=""&gt;

&lt;/figure&gt;

&lt;h3 id="language-note"&gt;Language Note&lt;/h3&gt;

&lt;p&gt;“vest” means a coat with arms in Dutch. In UK English, a “vest” is a formal button vest to wear with suits whereas a vest like this is called a “gilet” (zhee-lee), borrowed from French.&lt;/p&gt;

</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20260826_dutch_buddy/</id>
    <title>Dutch Buddy: an iOS app for practicing my Dutch</title>
    <published>2026-08-26T00:00:00Z</published>
    <updated>2026-08-26T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20260826_dutch_buddy/" type="text/html"/>
    <content type="html">&lt;h1 id="dutch-buddy-an-ios-app-for-practicing-my-dutch"&gt;Dutch Buddy: an iOS app for practicing my Dutch&lt;/h1&gt;

&lt;p&gt;Dutch Buddy is the first iOS app I’ve ever made. Now that I have AI to help with the scaffolding and I don’t need to learn Swift myself, iOS isn’t as daunting as it used to be.&lt;/p&gt;

&lt;p&gt;The app is not available publicly; I load it onto my phone every week with Xcode. =p&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/nickivance/dutch-buddy"&gt;dutch-buddy on Github&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="realtime-chatgpt"&gt;Realtime ChatGPT&lt;/h2&gt;

&lt;p&gt;I was motivated to build this app after testing out the realtime chat functionality of ChatGPT to practice my Dutch. I love the flow of being able to talk while I was walking, not needing to look at a screen.&lt;/p&gt;

&lt;p&gt;The ChatGPT realtime models sit behind a streaming connection that 
+ buffers audio, 
+ detects “turns” or breaks in the conversation where the speaker pauses to indicate a reply is expected, 
+ translates audio to text, 
+ writes a response 
+ which is then converted text-to-speech so that I can hear the audio response.&lt;/p&gt;

&lt;p&gt;It’s amazing that this can happen fast enough to feel relatively natural. In English, at least.&lt;/p&gt;

&lt;h2 id="multi-language-issues"&gt;Multi-Language Issues&lt;/h2&gt;

&lt;p&gt;There are a couple problems, however, when I try to speak in Dutch.&lt;/p&gt;

&lt;h3 id="turn-detection"&gt;Turn Detection&lt;/h3&gt;

&lt;p&gt;First, I am a newbie which means that I have great big pauses while I speak Dutch as I wrack my brain for a certain word. The default settings on ChatGPT’s realtime chat are set to detect a conversation turn after a short pause. When I (frequently) exceed that short pause, ChatGPT stops listening, prepares its own response and starts replying to my half-finished sentence.&lt;/p&gt;

&lt;h3 id="language-setting"&gt;Language Setting&lt;/h3&gt;
&lt;p&gt;Second, there are times when I want to see the transcript of what I just said or what the agent just said to me.&lt;/p&gt;

&lt;p&gt;When I open the app to look at the history of my verbal conversation with ChatGPT, my portions of the conversation are transcribed in English, not Dutch. This is because the app is using two different models to build the realtime chat experience.&lt;/p&gt;

&lt;p&gt;While the realtime model is receiving buffered audio files that it then transcribes, the app has a separate process that collects the audio once the end-of-turn is detected and sends that audio to a different transcription model through an async API call. This process is configured to use English (probably based on my user preferences gathered from my operating system when I initially signed up) so when it receives audio other than English, it translates and transcribes.&lt;/p&gt;

&lt;p&gt;I need the transcript of my own speech to stay true to the language I’m speaking in, Dutch.&lt;/p&gt;

&lt;p&gt;As a side note, there is a lot more to say on this topic about mixed language usage, for example when I use an English word in the middle of a Dutch sentence because I don’t know the Dutch word. Detecting language switches in audio is tricky.&lt;/p&gt;

&lt;h3 id="please-repeat"&gt;Please Repeat&lt;/h3&gt;
&lt;p&gt;Third, as a language learner, it’s really helpful to hear something repeated. When I ask the realtime chat to repeat itself (“nog een keer”), it goes through the loop of creating a new response which is never exactly the same. Usually it’s a rephrasing because the chat conversation loop has no mechanism for exactly repeating itself. It simply wasn’t designed for this use case. In this scenario, ideally it would replay the audio of its last response.&lt;/p&gt;

&lt;h3 id="smoothing-over-mistakes"&gt;Smoothing Over Mistakes&lt;/h3&gt;

&lt;p&gt;There is also a gap sometimes when I am fumbling a word or don’t pronounce it correctly. I’ve seen this either result in a misunderstanding (the model is going to guess something rather than ask me to repeat myself) or, just as unfortunate, smoothing over my mistakes as the model guesses correctly based on sentence context (correcting “getrouwen” to “betrouwen” means that I will continue asking people if they trust each other when I mean to ask if they are engaged).&lt;/p&gt;

&lt;h2 id="dutch-buddy"&gt;Dutch Buddy&lt;/h2&gt;

&lt;p&gt;The app I’ve built came together very quickly.&lt;/p&gt;

&lt;p&gt;The main function is to have a conversation. This functionality kicks off a chat using a websocket to a realtime model (&lt;code&gt;gpt-realtime-1.5&lt;/code&gt;). The language is set to Dutch and there is a prompt that emphasizes staying in Dutch and instructs the agent to give feedback appropriate for my level of Dutch.&lt;/p&gt;

&lt;p&gt;Similar to ChatGPT’s realtime chat functionality, audio clips are asynchronously sent off for transcription (&lt;code&gt;gpt-4o-transcribe&lt;/code&gt;) at each turn so that the UI has a record to show.&lt;/p&gt;

&lt;p&gt;For Text-to-Speech, I’m using &lt;code&gt;tts-1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The chat also has instructions to detect a “please repeat” command and when detected, to replay the stored TTS audio clip from the most recent response, rather than going through the chat response loop again.&lt;/p&gt;

&lt;h3 id="model-exploration"&gt;Model Exploration&lt;/h3&gt;
&lt;p&gt;I tried out a few different models for each distinct task. For example, &lt;code&gt;whisper-1&lt;/code&gt; was the first model I tried for STT transcription and I found it just wasn’t great with Dutch. &lt;code&gt;gpt-4o-mini-transcribe&lt;/code&gt; worked better and lately I’ve upgraded to &lt;code&gt;gpt-4o-transcribe&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I used &lt;code&gt;gpt-realtime-mini&lt;/code&gt; for a little while but I think the mini just didn’t have sufficient skills. For a short time, I tried using it for both streaming conversation and producing a transcript. But I it wasn’t made for transcription, it was too strongly inclined to respond, even with a clear prompt to transcribe only.&lt;/p&gt;

&lt;p&gt;OpenAI released &lt;code&gt;gpt-realtime-1.5&lt;/code&gt; and I’m much happier with it. I use it for the streaming conversation and &lt;code&gt;gpt-4o-transcribe&lt;/code&gt; for the transcript in the UI.&lt;/p&gt;

&lt;p&gt;So far, cost hasn’t been an influential criteria for model choice, probably since it’s just me testing.&lt;/p&gt;
&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20260826_dutch_buddy/IMG_5964.png" alt="" style="max-height: 600px; width: auto; height: auto; max-width: 100%; align-self: center;"&gt;

&lt;/figure&gt;

&lt;h2 id="daily-speaking-feature"&gt;Daily Speaking Feature&lt;/h2&gt;

&lt;p&gt;Recently, I’ve added a second feature: Daily Speaking.&lt;/p&gt;

&lt;p&gt;My Dutch language teacher asks us at the beginning of each course to come up with a goal for the course (my first one was to make a slide about a different bird every day). This time, I decided I wanted to work on my speaking by doing a short off-the-cuff monologue everyday. Say, 2 minutes, to make sure it doesn’t feel overwhelming.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20260826_dutch_buddy/IMG_5966.png" alt="" style="max-height: 600px; width: auto; height: auto; max-width: 100%; align-self: center;"&gt;

&lt;/figure&gt;

&lt;p&gt;My UX design includes the following:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generate a Prompt&lt;/strong&gt;
Optionally, you can generate a prompt if you don’t know what to talk about. This generates a prompt in Dutch, of course.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Record Audio&lt;/strong&gt;
Begin the recording. The recorder shows how long it has been recording and changes color when you’ve reached 2 minutes. You can stop anytime and continue as long as you like (or until the app hits some limit I haven’t reached).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transcribe&lt;/strong&gt;
When you stop the recording, a Dutch transcription is immediately generated using &lt;code&gt;gpt-4o-transcribe&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The transcription is an important part of the process because I want to review it to look for mistakes I made, the heart of the learning process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flag Mistakes&lt;/strong&gt;
I created a text input area (with optional speech input) to identify my own mistakes. I also note transcription mistakes but I don’t currently have a feedback loop for using that data. Maybe in the future I will store the transcription mistakes separately as data I could somehow use to improve the process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Review&lt;/strong&gt;
I have a review area where I can type or speak or just tap a button to request a review. The conversation model (&lt;code&gt;gpt-4o-mini&lt;/code&gt;) reviews the transcript and replies with a list of fixes and acknowledges and corrects the ones I flagged.&lt;/p&gt;

&lt;p&gt;So far this is working great. It’s very useful in catching my grammar mistakes with word order, mismatched singular/plurals, verb tense, etc.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20260826_dutch_buddy/IMG_5967.png" alt="" style="max-height: 600px; width: auto; height: auto; max-width: 100%; align-self: center;"&gt;

&lt;/figure&gt;

&lt;p&gt;For those that are curious, this app was built with Cursor in the first half of 2026. I leave Cursor on Auto model selection so I don’t know which models it was working with.&lt;/p&gt;

&lt;h2 id="did-the-app-work"&gt;Did the app work?&lt;/h2&gt;

&lt;p&gt;Sadly, I have to admit I’m not using the conversation feature. I’ve been frustrated with the mistakes the models are making.&lt;/p&gt;

&lt;p&gt;I began building out a set of evals with recordings of my own speech (just 10 recordings to start with) to quantify testing but honestly I have run out of steam on that task. It’s not the kind of work I love doing.&lt;/p&gt;

&lt;p&gt;I am really happy with the Daily Speaking feature - the failure there is just like any habit-building, I’m still trying to find a way to sit down and do it every day. =p&lt;/p&gt;

</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20260202_backup_research/</id>
    <title>CLI Backup Tools: Restic, Borg, Kopia</title>
    <published>2026-02-02T00:00:00Z</published>
    <updated>2026-02-02T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20260202_backup_research/" type="text/html"/>
    <content type="html">&lt;h1 id="cli-backup-tools-restic-borg-kopia"&gt;CLI Backup Tools: Restic, Borg, Kopia&lt;/h1&gt;



&lt;p&gt;Time for some early spring cleaning.&lt;/p&gt;

&lt;p&gt;I’ve got a shoddy backup system in place that needs to be revamped. Dropbox (which can’t be fully downloaded onto my laptop, its so large) and five external hard drives, each a snapshot of the Dropbox drive at a different point over the last 10 years and with some other random things thrown onto them.&lt;/p&gt;

&lt;p&gt;I used to have Backblaze but quit it years ago for some reason I don’t remember.&lt;/p&gt;

&lt;p&gt;I’d like to follow the 3-2-1 Backup Rule:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;3 copies of my data&lt;/li&gt;
  &lt;li&gt;2 different media (e.g. cloud and external hard drive)&lt;/li&gt;
  &lt;li&gt;1 copy off-site&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I actually have all this covered but not in a consistent fashion – none of the 3 copies are the same or all up-to-date. My off-site drive is in Seattle and hasn’t been updated for at least 3 years.&lt;/p&gt;

&lt;p&gt;If I could fit my Dropbox onto my laptop then I would maybe do the hacky things and periodically create a whole copy to two empty external hard drives, recycling the drives for each update. But I can’t anymore.&lt;/p&gt;

&lt;p&gt;I want a tool that helps me make backups with deduplication, so I can add to an existing backup rather than make a whole new copy. I don’t want to have to hop around between backups because my 2022 photos are in one backup and my 2023 photos are in another.&lt;/p&gt;

&lt;h3 id="my-use-cases"&gt;My Use Cases&lt;/h3&gt;

&lt;p&gt;I’m not doing anything too fancy nor are the stakes super high.&lt;/p&gt;

&lt;p&gt;There are three things I want to be able to do easily:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Maintain a backup of my data that I can browse occasionally.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Backup my recent documents and photos on a monthly basis.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Browse and tag my photos.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I have Dropbox and iPhoto both backing up my recent photos from my phone. And any recent documents on my laptop that could be lost between monthly backups is probably in my email or another cloud service, nor is it super important if I didn’t already put it in Dropbox, we’re basically talking my Downloads folder and the random screenshots I like to take of good or bad UX on the web that I never actually look at again.&lt;/p&gt;

&lt;h3 id="testing-backup-tools"&gt;Testing backup tools&lt;/h3&gt;
&lt;p&gt;I’m testing out three open source command line tools to see which one I like best.&lt;/p&gt;

&lt;p&gt;I don’t trust any company fully with my data and it sounds like the open source tools do a solid job, so why not go with free?&lt;/p&gt;

&lt;p&gt;I had ChatGPT come up with a very basic testing script so I could get a feel for each tool. Basically, it’s a directory with a couple files that I backup, make some changes, backup again, and restore from the original backup. Making some changes allows me to see how the tool handles deduping.&lt;/p&gt;

&lt;div class="language-sh highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;mkdir -p ~/backup-test/{src1,src2,src3}
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;cd ~/backup-test
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;echo "file A" &amp;gt; src1/a.txt
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;echo "file B" &amp;gt; src1/b.txt
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;echo "file C" &amp;gt; src1/c.txt
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;h3 id="restic"&gt;Restic&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://restic.readthedocs.io/en/stable/index.html"&gt;Restic Documentation — restic 0.18.1 documentation&lt;/a&gt;&lt;br&gt;Language: Go&lt;/p&gt;

&lt;div class="language-sh highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;brew install restic
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;export RESTIC_REPOSITORY=/backup-test #doesn't have to be the same dir
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;export RESTIC_PASSWORD=testpassword
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;restic init
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n7" name="n7"&gt;7&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n8" name="n8"&gt;8&lt;/a&gt;&lt;/span&gt;restic backup src1
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n9" name="n9"&gt;9&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;# only the changes will be backed up
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n11" name="n11"&gt;11&lt;/a&gt;&lt;/span&gt;rm src1/b.txt
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n12" name="n12"&gt;12&lt;/a&gt;&lt;/span&gt;echo "file D" &amp;gt; src1/d.txt
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n13" name="n13"&gt;13&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n14" name="n14"&gt;14&lt;/a&gt;&lt;/span&gt;restic backup src1
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n15" name="n15"&gt;15&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n16" name="n16"&gt;16&lt;/a&gt;&lt;/span&gt;repository 3de9effd opened (version 2, compression level auto)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n17" name="n17"&gt;17&lt;/a&gt;&lt;/span&gt;created new cache in /Users/boo/Library/Caches/restic
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n18" name="n18"&gt;18&lt;/a&gt;&lt;/span&gt;no parent snapshot found, will read all files
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n19" name="n19"&gt;19&lt;/a&gt;&lt;/span&gt;[0:00]          0 index files loaded
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n20" name="n20"&gt;20&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n21" name="n21"&gt;21&lt;/a&gt;&lt;/span&gt;Files:           3 new,     0 changed,     0 unmodified
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n22" name="n22"&gt;22&lt;/a&gt;&lt;/span&gt;Dirs:            1 new,     0 changed,     0 unmodified
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n23" name="n23"&gt;23&lt;/a&gt;&lt;/span&gt;Added to the repository: 1.805 KiB (1.113 KiB stored)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n24" name="n24"&gt;24&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n25" name="n25"&gt;25&lt;/a&gt;&lt;/span&gt;processed 3 files, 21 B in 0:00
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n26" name="n26"&gt;26&lt;/a&gt;&lt;/span&gt;snapshot c6277153 saved
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n27" name="n27"&gt;27&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n28" name="n28"&gt;28&lt;/a&gt;&lt;/span&gt;# copy the current backup to another dir and add a file to test deduping
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n29" name="n29"&gt;29&lt;/a&gt;&lt;/span&gt;cp -r src1 src2
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n30" name="n30"&gt;30&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;echo "file E" &amp;gt; src2/e.txt
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n31" name="n31"&gt;31&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n32" name="n32"&gt;32&lt;/a&gt;&lt;/span&gt;restic backup src2
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n33" name="n33"&gt;33&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n34" name="n34"&gt;34&lt;/a&gt;&lt;/span&gt;repository 3de9effd opened (version 2, compression level auto)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n35" name="n35"&gt;35&lt;/a&gt;&lt;/span&gt;using parent snapshot c6277153
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n36" name="n36"&gt;36&lt;/a&gt;&lt;/span&gt;[0:00] 100.00%  1 / 1 index files loaded
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n37" name="n37"&gt;37&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n38" name="n38"&gt;38&lt;/a&gt;&lt;/span&gt;Files:           1 new,     0 changed,     2 unmodified
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n39" name="n39"&gt;39&lt;/a&gt;&lt;/span&gt;Dirs:            0 new,     1 changed,     0 unmodified
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n40" name="n40"&gt;40&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;Added to the repository: 1.791 KiB (970 B stored)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n41" name="n41"&gt;41&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n42" name="n42"&gt;42&lt;/a&gt;&lt;/span&gt;processed 3 files, 21 B in 0:00
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n43" name="n43"&gt;43&lt;/a&gt;&lt;/span&gt;snapshot 3d20c1b2 saved
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n44" name="n44"&gt;44&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n45" name="n45"&gt;45&lt;/a&gt;&lt;/span&gt;# list snapshots
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n46" name="n46"&gt;46&lt;/a&gt;&lt;/span&gt;restic snapshots
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n47" name="n47"&gt;47&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n48" name="n48"&gt;48&lt;/a&gt;&lt;/span&gt;repository 3de9effd opened (version 2, compression level auto)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n49" name="n49"&gt;49&lt;/a&gt;&lt;/span&gt;ID        Time                 Host                        Tags   Paths                                Size
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n50" name="n50"&gt;50&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;---------------------------------------------------------------------------------------
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n51" name="n51"&gt;51&lt;/a&gt;&lt;/span&gt;c6277153  2026-01-30 16:53:12  Nickis-Laptop.local              /Users/boo/dev/backup-test/src1  21 B
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n52" name="n52"&gt;52&lt;/a&gt;&lt;/span&gt;3d20c1b2  2026-01-30 16:59:52  Nickis-Laptop.local              /Users/boo/dev/backup-test/src1  21 B
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n53" name="n53"&gt;53&lt;/a&gt;&lt;/span&gt;3a9d7c2d  2026-01-30 17:00:05  Nickis-Laptop.local              /Users/boo/dev/backup-test/src2  28 B
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n54" name="n54"&gt;54&lt;/a&gt;&lt;/span&gt;d55cba5e  2026-01-30 17:00:30  Nickis-Laptop.local              /Users/boo/dev/backup-test/src3  12 B
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n55" name="n55"&gt;55&lt;/a&gt;&lt;/span&gt;---------------------------------------------------------------------------------------
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n56" name="n56"&gt;56&lt;/a&gt;&lt;/span&gt;4 snapshots
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n57" name="n57"&gt;57&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n58" name="n58"&gt;58&lt;/a&gt;&lt;/span&gt;# restore a snapshot
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n59" name="n59"&gt;59&lt;/a&gt;&lt;/span&gt;restic restore  --target /tmp/restore-work {snapshot ID}
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n60" name="n60"&gt;60&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n61" name="n61"&gt;61&lt;/a&gt;&lt;/span&gt;repository 3de9effd opened (version 2, compression level auto)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n62" name="n62"&gt;62&lt;/a&gt;&lt;/span&gt;[0:00] 100.00%  4 / 4 index files loaded
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n63" name="n63"&gt;63&lt;/a&gt;&lt;/span&gt;restoring snapshot c6277153 of [/Users/boo/dev/backup-test/src1] at 2026-01-30 16:53:12.927071 +0100 CET by boo@Nickis-Laptop.local to /tmp/restore-work
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n64" name="n64"&gt;64&lt;/a&gt;&lt;/span&gt;Summary: Restored 4 files/dirs (21 B) in 0:00
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n65" name="n65"&gt;65&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n66" name="n66"&gt;66&lt;/a&gt;&lt;/span&gt;ls /tmp/restore-work
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n67" name="n67"&gt;67&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n68" name="n68"&gt;68&lt;/a&gt;&lt;/span&gt;rm -rf /tmp/restore-work
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;I like the restic commands the best of the three I’ve tried, mainly because I don’t have to name the backup.&lt;/p&gt;

&lt;h3 id="borghttpswwwborgbackuporg"&gt;&lt;a href="https://www.borgbackup.org/"&gt;Borg&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Language: Python&lt;/p&gt;

&lt;p&gt;&lt;code&gt;borg&lt;/code&gt; uses the term “archive” where &lt;code&gt;restic&lt;/code&gt; uses “snapshot”.&lt;/p&gt;

&lt;div class="language-sh highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;export BORG_REPO=/Users/boo/dev/backup-test-2/repo #needs to be an empty dir
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;export BORG_PASSPHRASE=testpassword
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;borg init --encryption=repokey
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;IMPORTANT: you will need both KEY AND PASSPHRASE to access this repo!
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n7" name="n7"&gt;7&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n8" name="n8"&gt;8&lt;/a&gt;&lt;/span&gt;Key storage location depends on the mode:
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n9" name="n9"&gt;9&lt;/a&gt;&lt;/span&gt;- repokey modes: key is stored in the repository directory.
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;- keyfile modes: key is stored in the home directory of this user.
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n11" name="n11"&gt;11&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n12" name="n12"&gt;12&lt;/a&gt;&lt;/span&gt;For any mode, you should:
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n13" name="n13"&gt;13&lt;/a&gt;&lt;/span&gt;1. Export the borg key and store the result at a safe place:
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n14" name="n14"&gt;14&lt;/a&gt;&lt;/span&gt;borg key export           REPOSITORY encrypted-key-backup
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n15" name="n15"&gt;15&lt;/a&gt;&lt;/span&gt;borg key export --paper   REPOSITORY encrypted-key-backup.txt
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n16" name="n16"&gt;16&lt;/a&gt;&lt;/span&gt;borg key export --qr-html REPOSITORY encrypted-key-backup.html
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n17" name="n17"&gt;17&lt;/a&gt;&lt;/span&gt;2. Write down the borg key passphrase and store it at safe place.
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n18" name="n18"&gt;18&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n19" name="n19"&gt;19&lt;/a&gt;&lt;/span&gt;borg create $BORG_REPO::path/to/src1 src1
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n20" name="n20"&gt;20&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n21" name="n21"&gt;21&lt;/a&gt;&lt;/span&gt;borg list $BORG_REPO
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n22" name="n22"&gt;22&lt;/a&gt;&lt;/span&gt;src1     Sat, 2026-01-31 22:06:03 [e1bb98882f01e225e2a099aa83611fb91870ca5e5383d8391478c7b834e711ad]
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n23" name="n23"&gt;23&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n24" name="n24"&gt;24&lt;/a&gt;&lt;/span&gt;rm src1/c.txt
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n25" name="n25"&gt;25&lt;/a&gt;&lt;/span&gt;echo "file D" &amp;gt; src1/d.txt
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n26" name="n26"&gt;26&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n27" name="n27"&gt;27&lt;/a&gt;&lt;/span&gt;borg create $BORG_REPO::src1-2 src1
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n28" name="n28"&gt;28&lt;/a&gt;&lt;/span&gt;borg create $BORG_REPO::src2 src2
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n29" name="n29"&gt;29&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n30" name="n30"&gt;30&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;borg list $BORG_REPO
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n31" name="n31"&gt;31&lt;/a&gt;&lt;/span&gt;src1     Sat, 2026-01-31 22:06:03 [e1bb98882f01e225e2a099aa83611fb91870ca5e5383d8391478c7b834e711ad]
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n32" name="n32"&gt;32&lt;/a&gt;&lt;/span&gt;src1-2   Sat, 2026-01-31 22:10:25 [32b95f572f19ed6d103a184789c6aa060324789fa3b87570de66356dcb0ce0c1]
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n33" name="n33"&gt;33&lt;/a&gt;&lt;/span&gt;src2     Sat, 2026-01-31 22:12:42 [8a9fe564ab649e47736e1a01e81e116946bf74ca131e6e1b94b14b1ad2dea520]
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n34" name="n34"&gt;34&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n35" name="n35"&gt;35&lt;/a&gt;&lt;/span&gt;borg info
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n36" name="n36"&gt;36&lt;/a&gt;&lt;/span&gt;Repository ID: be5ca57233423b0ad2cc12b2a262b20790d1dde7b9a4f0c34a93740f234c39e7
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n37" name="n37"&gt;37&lt;/a&gt;&lt;/span&gt;Location: /Users/boo/dev/backup-test-2/repo
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n38" name="n38"&gt;38&lt;/a&gt;&lt;/span&gt;Encrypted: Yes (repokey)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n39" name="n39"&gt;39&lt;/a&gt;&lt;/span&gt;Cache: /Users/boo/.cache/borg/be5ca57233423b0ad2cc12b2a262b20790d1dde7b9a4f0c34a93740f234c39e7
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n40" name="n40"&gt;40&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;Security dir: /Users/boo/.config/borg/security/be5ca57233423b0ad2cc12b2a262b20790d1dde7b9a4f0c34a93740f234c39e7
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n41" name="n41"&gt;41&lt;/a&gt;&lt;/span&gt;------------------------------------------------------------------------------
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n42" name="n42"&gt;42&lt;/a&gt;&lt;/span&gt;                        Original size      Compressed size    Deduplicated size
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n43" name="n43"&gt;43&lt;/a&gt;&lt;/span&gt;All archives:                   42 B                300 B              2.87 kB
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n44" name="n44"&gt;44&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n45" name="n45"&gt;45&lt;/a&gt;&lt;/span&gt;                        Unique chunks         Total chunks
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n46" name="n46"&gt;46&lt;/a&gt;&lt;/span&gt;Chunk index:                      10                   12
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n47" name="n47"&gt;47&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n48" name="n48"&gt;48&lt;/a&gt;&lt;/span&gt;borg extract $BORG_REPO::src1-2
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The requirement to manually name each snapshot/archive could be either a pro or a con. The naming scheme I’ve used in the example above is terrible. I’m not sure what I’d want to use other than a date, which is repeated in the list.&lt;/p&gt;

&lt;p&gt;As you can see from the init process, a key is created. You need both the key and the passphrase to access a &lt;code&gt;borg&lt;/code&gt; repository.&lt;/p&gt;

&lt;p&gt;Interesting features to explore:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;compacting&lt;/li&gt;
  &lt;li&gt;compression&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="kopiahttpskopiaio"&gt;&lt;a href="https://kopia.io/"&gt;Kopia&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Language: Go&lt;/p&gt;

&lt;div class="language-sh highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;kopia repository create filesystem --path /Users/boo/dev/backup-test-3/repo
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;Enter password to create new repository: testpassword
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;Re-enter password for verification: testpassword
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;Initializing repository with:
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;block hash:          BLAKE2B-256-128
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;encryption:          AES256-GCM-HMAC-SHA256
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n7" name="n7"&gt;7&lt;/a&gt;&lt;/span&gt;key derivation:      scrypt-65536-8-1
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n8" name="n8"&gt;8&lt;/a&gt;&lt;/span&gt;splitter:            DYNAMIC-4M-BUZHASH
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20260202_backup_research/#n9" name="n9"&gt;9&lt;/a&gt;&lt;/span&gt;Connected to repository.
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n11" name="n11"&gt;11&lt;/a&gt;&lt;/span&gt;NOTICE: Kopia will check for updates on GitHub every 7 days, starting 24 hours after first use.
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n12" name="n12"&gt;12&lt;/a&gt;&lt;/span&gt;To disable this behavior, set environment variable KOPIA_CHECK_FOR_UPDATES=false
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n13" name="n13"&gt;13&lt;/a&gt;&lt;/span&gt;Alternatively you can remove the file "/Users/boo/Library/Application Support/kopia/repository.config.update-info.json".
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n14" name="n14"&gt;14&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n15" name="n15"&gt;15&lt;/a&gt;&lt;/span&gt;Retention:
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n16" name="n16"&gt;16&lt;/a&gt;&lt;/span&gt;Annual snapshots:                 3   (defined for this target)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n17" name="n17"&gt;17&lt;/a&gt;&lt;/span&gt;Monthly snapshots:               24   (defined for this target)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n18" name="n18"&gt;18&lt;/a&gt;&lt;/span&gt;Weekly snapshots:                 4   (defined for this target)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n19" name="n19"&gt;19&lt;/a&gt;&lt;/span&gt;Daily snapshots:                  7   (defined for this target)
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n20" name="n20"&gt;20&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;Hourly snapshots:                48   (defined for this target)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n21" name="n21"&gt;21&lt;/a&gt;&lt;/span&gt;Latest snapshots:                10   (defined for this target)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n22" name="n22"&gt;22&lt;/a&gt;&lt;/span&gt;Ignore identical snapshots:   false   (defined for this target)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n23" name="n23"&gt;23&lt;/a&gt;&lt;/span&gt;Compression disabled.
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n24" name="n24"&gt;24&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n25" name="n25"&gt;25&lt;/a&gt;&lt;/span&gt;To find more information about default policy run 'kopia policy get'.
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n26" name="n26"&gt;26&lt;/a&gt;&lt;/span&gt;To change the policy use 'kopia policy set' command.
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n27" name="n27"&gt;27&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n28" name="n28"&gt;28&lt;/a&gt;&lt;/span&gt;NOTE: Kopia will perform quick maintenance of the repository automatically every 1h0m0s
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n29" name="n29"&gt;29&lt;/a&gt;&lt;/span&gt;and full maintenance every 24h0m0s when running as boo@nickis-Laptop.
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n30" name="n30"&gt;30&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n31" name="n31"&gt;31&lt;/a&gt;&lt;/span&gt;See https://kopia.io/docs/advanced/maintenance/ for more information.
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n32" name="n32"&gt;32&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n33" name="n33"&gt;33&lt;/a&gt;&lt;/span&gt;NOTE: To validate that your provider is compatible with Kopia, please run:
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n34" name="n34"&gt;34&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n35" name="n35"&gt;35&lt;/a&gt;&lt;/span&gt;$ kopia repository validate-provider
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n36" name="n36"&gt;36&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n37" name="n37"&gt;37&lt;/a&gt;&lt;/span&gt;kopia snapshot create /Users/boo/dev/backup-test-3/backup_dir
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n38" name="n38"&gt;38&lt;/a&gt;&lt;/span&gt;Snapshotting boo@nickis-Laptop:/Users/boo/dev/backup-test-3/backup_dir ...
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n39" name="n39"&gt;39&lt;/a&gt;&lt;/span&gt;* 0 hashing, 3 hashed (21 B), 0 cached (0 B), uploaded 211 B, estimating...
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n40" name="n40"&gt;40&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;Created snapshot with root k8631e4a74fa1bfb7327d82b503a46a8b and ID 1f6bf45929328b1372b1d8b37237e294 in 0s
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n41" name="n41"&gt;41&lt;/a&gt;&lt;/span&gt;Running full maintenance...
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n42" name="n42"&gt;42&lt;/a&gt;&lt;/span&gt;GC found 0 unused contents (0 B)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n43" name="n43"&gt;43&lt;/a&gt;&lt;/span&gt;GC found 0 unused contents that are too recent to delete (0 B)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n44" name="n44"&gt;44&lt;/a&gt;&lt;/span&gt;GC found 7 in-use contents (1.1 KB)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n45" name="n45"&gt;45&lt;/a&gt;&lt;/span&gt;GC found 2 in-use system-contents (1.2 KB)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n46" name="n46"&gt;46&lt;/a&gt;&lt;/span&gt;GC undeleted 0 contents (0 B)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n47" name="n47"&gt;47&lt;/a&gt;&lt;/span&gt;Compacting an eligible uncompacted epoch...
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n48" name="n48"&gt;48&lt;/a&gt;&lt;/span&gt;Advancing epoch markers...
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n49" name="n49"&gt;49&lt;/a&gt;&lt;/span&gt;Attempting to compact a range of epoch indexes ...
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n50" name="n50"&gt;50&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;Cleaning up unneeded epoch markers...
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n51" name="n51"&gt;51&lt;/a&gt;&lt;/span&gt;Cleaning up old index blobs which have already been compacted...
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n52" name="n52"&gt;52&lt;/a&gt;&lt;/span&gt;Cleaned up 0 logs.
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n53" name="n53"&gt;53&lt;/a&gt;&lt;/span&gt;Finished full maintenance.
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n54" name="n54"&gt;54&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n55" name="n55"&gt;55&lt;/a&gt;&lt;/span&gt;kopia snapshot list /Users/boo/dev/backup-test-3/backup_dir
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n56" name="n56"&gt;56&lt;/a&gt;&lt;/span&gt;boo@nickis-Laptop:/Users/boo/dev/backup-test-3/backup_dir
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n57" name="n57"&gt;57&lt;/a&gt;&lt;/span&gt;2026-02-01 10:33:07 CET k8631e4a74fa1bfb7327d82b503a46a8b 21 B drwxr-xr-x files:3 dirs:4 (latest-1..2,hourly-1,daily-1,weekly-1,monthly-1,annual-1)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n58" name="n58"&gt;58&lt;/a&gt;&lt;/span&gt;- 1 identical snapshots until 2026-02-01 10:33:58 CET
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n59" name="n59"&gt;59&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n60" name="n60"&gt;60&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;kopia ls -l k8631e4a74fa1bfb7327d82b503a46a8b
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n61" name="n61"&gt;61&lt;/a&gt;&lt;/span&gt;drwxr-xr-x           21 2026-01-31 22:14:47 CET k6647c300e69cecccd21f920519c29abb  src1/
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n62" name="n62"&gt;62&lt;/a&gt;&lt;/span&gt;drwxr-xr-x            0 2026-01-31 22:14:40 CET k1f7f41f99d34ce2bda1e9b82182cec5e  src2/
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n63" name="n63"&gt;63&lt;/a&gt;&lt;/span&gt;drwxr-xr-x            0 2026-01-31 22:14:40 CET kf1038421ff24d338a963ef2deef7bc44  src3/
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n64" name="n64"&gt;64&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n65" name="n65"&gt;65&lt;/a&gt;&lt;/span&gt;kopia show k6647c300e69cecccd21f920519c29abb
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20260202_backup_research/#n66" name="n66"&gt;66&lt;/a&gt;&lt;/span&gt;{"stream":"kopia:directory","entries":[{"name":"a.txt","type":"f","mode":"0644","size":7,"mtime":"2026-01-31T21:14:47.012334056Z","uid":501,"gid":20,"obj":"694bbedb98415f944e757fb04da85a00"},{"name":"b.txt","type":"f","mode":"0644","size":7,"mtime":"2026-01-31T21:14:47.012475682Z","uid":501,"gid":20,"obj":"680c1e63c73751bf535d6f56c1904958"},{"name":"c.txt","type":"f","mode":"0644","size":7,"mtime":"2026-01-31T21:14:47.012585432Z","uid":501,"gid":20,"obj":"cf894e34efbbdea5902193cd762aa89f"}],"summary":{"size":21,"files":3,"symlinks":0,"dirs":1,"maxTime":"2026-01-31T21:14:47.012585432Z","numFailed":0}}
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;I’d recommend creating env variables for &lt;code&gt;kopia&lt;/code&gt; because it requires the use of absolute paths.&lt;/p&gt;

&lt;p&gt;Interesting features to explore:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;retention policies&lt;/li&gt;
  &lt;li&gt;automated backups&lt;/li&gt;
  &lt;li&gt;compression&lt;/li&gt;
  &lt;li&gt;ignore rules&lt;/li&gt;
  &lt;li&gt;GUI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After just a quick glance, &lt;code&gt;kopia&lt;/code&gt; is exciting because it appears to have some great features for more complex use cases. There appears to be multiple contributors and an active user community. I like, for example, that there is a discourse forum (but unfortunately also a Slack). Complexity, on the other hand, often means more interdependencies and therefore higher risks.&lt;/p&gt;

&lt;h3 id="next-steps"&gt;Next Steps&lt;/h3&gt;
&lt;p&gt;I’m going to move forward with &lt;code&gt;borg&lt;/code&gt; mainly because my partner also uses it (aka in-house help desk).&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Set up two external hard drives with a full copy of Dropbox&lt;/li&gt;
  &lt;li&gt;Set up borg repos on the drives and my laptop&lt;/li&gt;
  &lt;li&gt;Create a script to run manually once a month&lt;/li&gt;
  &lt;li&gt;Test it out for a couple months, including my photo browsing &amp;amp; metadata update workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: Here’s why syncing a backup repo isn’t recommended: &lt;a href="https://borgbackup.readthedocs.io/en/1.4-maint/faq.html#can-i-copy-or-synchronize-my-repo-to-another-location"&gt;Can I copy or synchronize my repo to another location?&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20260117_mystery_decorations/</id>
    <title> Insect Houses as Building Decorations in Amsterdam</title>
    <published>2026-01-17T00:00:00Z</published>
    <updated>2026-01-17T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20260117_mystery_decorations/" type="text/html"/>
    <content type="html">&lt;h1 id="insect-houses-as-building-decorations-in-amsterdam"&gt;Insect Houses as Building Decorations in Amsterdam&lt;/h1&gt;

&lt;p&gt;I love building decoration. It’s one of the differences between the US and many older countries that I haven’t grown blind to. I take pictures constantly of the many stone characters, brickwork patterns, even the mailslots, that grace the buildings here.&lt;/p&gt;

&lt;p&gt;These started to catch my eye and I’ve collected 4 so far. They’re gorgeous.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20260117_mystery_decorations/2024-02-18.jpeg" alt=""&gt;

&lt;/figure&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20260117_mystery_decorations/2025-05-21.jpeg" alt=""&gt;

&lt;/figure&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20260117_mystery_decorations/2025-05-31.jpeg" alt=""&gt;

  &lt;figcaption&gt;This one's my favorite, possibly because its so well customized to its surroundings.&lt;/figcaption&gt;

&lt;/figure&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20260117_mystery_decorations/2025-08-18.jpeg" alt=""&gt;

&lt;/figure&gt;

&lt;p&gt;These have been a mystery to me for a couple years. They don’t seem to be part of the original building because the material doesn’t exactly match and they don’t repeat like other decorative elements on the buildings. And the small scale is a giveaway. I wondered if they were some makers mark, perhaps from the Stadgenoot that owns and renovates many buildings (their mark happened to be on these buildings but its just a coincidence). Or maybe the best form of street art I’ve ever seen?&lt;/p&gt;

&lt;p&gt;Now that I’ve sat down to write about them, I’ve finally put my photo into Google Reverse Image search and found the answer on the first try: they’re insect houses. But also art, like I thought, but they have an additional purpose.&lt;/p&gt;

&lt;p&gt;These insect houses are created by artist Bastiaan Meijer who continues to make them. Luckily, he’s got his own website explaining the project: &lt;a href="https://www.insectenhuisjes.nl/paginas/wat.html"&gt;wat zijn insectenhuisjes&lt;/a&gt; The site also includes a map of all the locations where the insect houses have been installed. There are between 1 and 2 thousand.&lt;/p&gt;

&lt;p&gt;I haven’t done a full survey of the insect houses but many of them, including the ones that caught my eye, are inspired or modeled after the &lt;a href="https://en.wikipedia.org/wiki/Amsterdam_School"&gt;Amsterdamse School&lt;/a&gt; architectural movement. I’m a big fan of this architectural style because it brings a beautiful layer to Amsterdam. A lot of cities may have a quaint, cozy old town in the center and everything else is new. Amsterdam architecture is organized like tree rings, each one surrounded the existing city when it was developed. The Amsterdamse School ring of urban growth dates from the 1910s-1930s.&lt;/p&gt;

&lt;p&gt;If you’re visiting the city and are interested in architecture, I highly recommend visiting &lt;a href="https://www.hetschip.nl/"&gt;Museum Het Schip&lt;/a&gt; to see more fine examples.&lt;/p&gt;

&lt;p&gt;I’m so happy to learn that there are many more of these insect houses out there to discover.&lt;/p&gt;

&lt;p&gt;More examples on Bastiaan Meijer’s &lt;a href="https://www.youtube.com/@bastiaaninsectenhuisjes"&gt;YouTube channel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;hr style="width: 100%"&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;De kleine huisjes op de foto’s hierboven waren een mysterie voor me. Ik vind de stijl erg grillig en ze vereren de beroemde Amsterdamse School architectuurstijl. Ik dacht eerst dat het straatkunst was. Ik ben blij te ontdekken dat de kunst van een lokale kunstenaar is gemaakt.&lt;/p&gt;

&lt;p&gt;De kunstenaar heet Bastiaan Meijer en hij maakt de huisjes voor insecten. De huisjes moedigen meer verschillende insecten aan en vergroten biodiversiteit. Insecten zijn belangrijk omdat ze planten en bomen bestuiven en helpen bij de afbraak van afval.&lt;/p&gt;

&lt;p&gt;Wat een slim idee! Biodiversiteit en schoonheid gecombineerd.&lt;/p&gt;

&lt;p&gt;Als je meer wilt leren, bezoek de webpagina van de kunstenaar: &lt;a href="https://www.insectenhuisjes.nl/paginas/wat.html"&gt;wat zijn insectenhuisjes&lt;/a&gt;. De kunstenaar geeft één of twee keer per jaar workshops over het maken van insectenhuisjes, waar je je voor kunt aanmelden.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20230713_summer_exhibition/</id>
    <title>2023 Summer Exhibition</title>
    <published>2023-07-13T00:00:00Z</published>
    <updated>2023-07-13T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20230713_summer_exhibition/" type="text/html"/>
    <content type="html">&lt;h1 id="summer-exhibition"&gt;2023 Summer Exhibition&lt;/h1&gt;

&lt;p&gt;I wish I remembered who first told me about the Royal Academy of Arts Summer Exhibition so I could give them a big squeezy hug. This was easily one of my favorite experiences of the year.&lt;/p&gt;

&lt;figure class="img-full"&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_101623_2023-07-13_09.45.15_crop.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_101623_2023-07-13_09.45.15_crop.jpg" alt="A statue of an artist in front of the Summer Exhibition banner over the entrance of Burlington House."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;The iconic entrance to the Burlington House, where the Summer Exhibition takes place each year.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="whats-the-summer-exhibition"&gt;What’s the Summer Exhibition?&lt;/h2&gt;

&lt;p&gt;The Summer Exhibition is an annual open submission art exhibition coordinated by the Royal Academy of Arts in London.&lt;/p&gt;

&lt;p&gt;Open submission means anyone can submit (though few are chosen); the works displayed are created by a range of people - experienced, established artists, new talent, or a first time work from anyone. I think this wide range of sources is a large part of why I feel such a strong connection to this event – I can imagine someone like myself made some of these works.&lt;/p&gt;

&lt;p&gt;I also find it fascinating that almost all these works are for sale. Proceeds benefit the Royal Academy of Arts to support its various charitable activities.&lt;/p&gt;

&lt;h2 id="by-the-numbers"&gt;By the Numbers&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;The first was hosted in 1769.&lt;/li&gt;
  &lt;li&gt;This year’s exhibition is the 255th.&lt;/li&gt;
  &lt;li&gt;It runs through the summer, this year from June 13 to August 20. I visited in mid-July.&lt;/li&gt;
  &lt;li&gt;11,204 works were submitted to the open call (total submissions were capped at 16,500).&lt;/li&gt;
  &lt;li&gt;A submission costs £38.&lt;/li&gt;
  &lt;li&gt;1,613 works were exhibited, by 998 artists.&lt;/li&gt;
  &lt;li&gt;12 gallery rooms, plus a small side room.&lt;/li&gt;
  &lt;li&gt;Over £70,000 in prizes were awarded.&lt;/li&gt;
  &lt;li&gt;The lowest listed art for sale was £45.&lt;/li&gt;
  &lt;li&gt;The highest listed art for sale was £1,354,999 (it was authored by a comedian so I wonder if it’s a joke). The next highest priced artwork was £210,000. Many works were Not for Sale (NFS) and may have already been sold for more.&lt;/li&gt;
  &lt;li&gt;167,000 visitors attended the exhibition.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="first-impressions"&gt;First Impressions&lt;/h2&gt;

&lt;p&gt;It was 10am on a weekday when I visited so it wasn’t crowded. I enjoyed having the space to see the works and watch the galleries fill up with visitors.&lt;/p&gt;

&lt;figure class="img-medium img-center"&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_102050_Screenshot_2025-06-07_at_6.00.31_PM.png"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_102050_Screenshot_2025-06-07_at_6.00.31_PM.png" alt="Floor plan of the galleries for the Summer Exhibition."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;The layout of the galleries at the Summer Exhibition.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure class="img-medium img-center"&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_103412_2023-07-13_10.06.47.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_103412_2023-07-13_10.06.47.jpg" alt="A photo of a gallery room with paintings on the wall and skylights above a gilded vaulted ceiling."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;A view of the galleries. (Gallery III)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The first couple rooms I went through seemed like normal contemporary art galleries.&lt;/p&gt;

&lt;h2 id="architecture"&gt;Architecture&lt;/h2&gt;

&lt;p&gt;My interest ratcheted up in the Architecture gallery because I’ve never seen architectural samples and models and sketches presented together in this way and I loved it.&lt;/p&gt;

&lt;p&gt;And, to be honest, I was very impressed with the bright green this gallery was painted. Shocked even. I’ve never seen a gallery painted a bright color.&lt;/p&gt;

&lt;figure class="img-medium img-center"&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_103744_2023-07-13_10.32.07.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_103744_2023-07-13_10.32.07.jpg" alt="A photo of the architecture gallery with bright green walls under a vaulted moulded ceiling, featuring an exposed cantilever made partially of a tree trunk and other modern materials."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;Wow, that green. And a cantilever!&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure class="img-medium img-center"&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_105540_2023-07-13_10.25.08.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_105540_2023-07-13_10.25.08.jpg" alt="Another angle of the same room, showing a tall column of large stacked boulders behind small architectural models."&gt;
    &lt;/a&gt;
&lt;/figure&gt;

&lt;p&gt;The architecture exhibits I’ve seen so far in my life have been showcasing finalized works through photos, sketches, and floor plans. Usually these focus on a specific building or an architect and try to emphasize the overall effect of the architecture.&lt;/p&gt;

&lt;p&gt;My favorite part of architecture is often the details - the materials, the textures, and the decoration. I was thrilled to see this wide range of architectural artifacts, which are often art on to themselves, and up close.&lt;/p&gt;

&lt;p&gt;From the &lt;a href="https://royal-academy-production-asset.s3.amazonaws.com/uploads/3f0769ae-c367-4bd6-8c9f-ffc75a34cee6/Summer%20Exhibition%202023%20(online).pdf"&gt;List of Works&lt;/a&gt; introduction to the gallery:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The architecture room has been curated by
Peter Barber RA. He has considered the overall
theme and decided to focus on sociologist
Richard Sennett’s assertion that ‘making is
thinking’, and through the physical craft of
making we have a better connection with the
world around us.
Barber has focused on architectural pieces
that are more analogue and crafted – a
product of the mind and the hand. Looming
large and cutting through the room is ‘Tree
and the Truss’, conceived by Design + Make,
Architectural Association Hooke Park.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="img-grid img-grid-2"&gt;
&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_105928_2023-07-13_10.25.38.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_105928_2023-07-13_10.25.38.jpg" alt="Photo of stonework detail of a pangolin."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;236: Pangolin grotesque for New College, Oxford, NFS, David Kohn Architectrs, Stoneraise sandstone&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_111104_2023-07-13_10.33.53.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_111104_2023-07-13_10.33.53.jpg" alt="Photo of two wooden towers approximately 2 meters tall."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;264: Moonlight towers, £30,000, American red oak, Jonah Luswata&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;

&lt;div class="img-grid img-grid-3"&gt;
&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_111153_2023-07-13_10.26.12.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_111153_2023-07-13_10.26.12.jpg" alt="Photo of multiple architectural models, drawings, material and joint samples mounted on a green wall."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;Models, samples, and joints.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_112342_2023-07-13_10.20.04.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_112342_2023-07-13_10.20.04.jpg" alt="A set of abstract plaster blocks with detail in front of a couple other architectural models."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;221: Barbican Centre: grain and scale, NFS, Plaster, Allies and Morrison&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_112406_2023-07-13_10.24.26.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_112406_2023-07-13_10.24.26.jpg" alt="Photo of a plaster model of a building with curved windows."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;219: A cultural venue to spark children’s creativity . (Sectional concept model)., NFS, 3d print, acrylic and chemiwood, Thomas Heatherwick RA&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;

&lt;h2 id="galleries"&gt;Galleries&lt;/h2&gt;

&lt;p&gt;I’d never really thought about what goes into hanging a gallery. I’d considered the expertise that is necessary to handle and hang the work, which can be much more challenging than the normal flat painting on a wall. I’d also considered archival challenges like room temperature and keeping oily fingers from touching. I’ve even taken a course on exhibit design as part of library school which included designing an exhibit guide - What is the story you want to tell? What viewpoints do you want to offer the visitor?&lt;/p&gt;

&lt;p&gt;Here, each gallery has been curated by one of 7 committee members. (I imagine a fight breaking out between committee members over the Darth Vadar piece.)&lt;/p&gt;

&lt;figure class="img-medium img-center"&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_153606_2023-07-13_10.41.06_crop.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_153606_2023-07-13_10.41.06_crop.jpg" alt="Photo of Gallery 5 with cream walls filled with paintings and drawings."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;Gallery V&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure class="img-medium img-center"&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_155402_2023-07-13_10.44.04_crop.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_155402_2023-07-13_10.44.04_crop.jpg" alt="A photo of a steel sculpture in the 5th gallery that looks like a giant paperclip painted bright colors and bent into a new shape."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;661: Frequency, £13,000, Stainless steel, Sam Shendi (Gallery V)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure class="img-medium img-center"&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_160823_2023-07-13_10.52.10.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_160823_2023-07-13_10.52.10.jpg" alt="Photo of what looks like a styrofoam box on the gallery floor."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;859: Veg box, £90,000, Painted bronze, Gavin Turk (Gallery VI). Rarely does an everyday object labeled "art" impress me, but the craftsmanship and the pique of replicating a styrofoam box in bronze was highly amusing. The jaunty angle of the lid is a nice touch. If my napkin math is correct, this box weighs over 800 kilos. 😁 I'd like to have it for a coffee table.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure class="img-medium img-center"&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_160938_2023-07-13_10.52.16.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_160938_2023-07-13_10.52.16.jpg" alt="A photo of a gallery wall with a dozen paintings displayed."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;I like the toilet painting in the bottom right. (Gallery VI)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure class="img-medium img-center"&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161005_2023-07-13_10.52.28.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161005_2023-07-13_10.52.28.jpg" alt="A wide angle photo showing the large doorway between two galleries."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;From a pinkish room to a lime green room. Note how the pieces are grouped by color and how that color is set off by the wall color.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure class="img-medium img-center"&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161500_2023-07-13_10.55.49.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161500_2023-07-13_10.55.49.jpg" alt="Deep blue or purple fabric draped over hoops hanging from the ceiling like a huge chandelier."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;1613: Poem in the dark about sadness, filíocht faoi bhrón, as an dorchadas. No price listed. Sculpture in hand bent metal bar, primer, high tension wire, hand sewn recycled jersey, polyamide, lycra, recycled silk, cotton thread. By Richard Malone. This is the headline piece that graced the exhibition marketing images. It looks like it would be a fun bird.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id="wall-colors"&gt;Wall Colors&lt;/h3&gt;

&lt;p&gt;Honestly, I was really surprised to see that the walls of the galleries weren’t just white. And I love thinking about how the curators for each gallery chose these colors.&lt;/p&gt;

&lt;p&gt;Some of these colors do a lot to bring out the colors in the pieces in a fascinating way.&lt;/p&gt;

&lt;p&gt;Here is a rough estimate of these colors, as a palette. I wouldn’t guess from this palette that it would work so well but I’m a color newbie. But since these are different rooms, they aren’t really competing with each other.&lt;/p&gt;
&lt;div style="display:flex"&gt;
&lt;div style="width:20%;min-height:100px;background:#c6c072"&gt;&lt;/div&gt;
&lt;div style="width:20%;min-height:100px;background:#ddb0bc"&gt;&lt;/div&gt;
&lt;div style="width:20%;min-height:100px;background:#fce8d8"&gt;&lt;/div&gt;
&lt;div style="width:20%;min-height:100px;background:#6dc684"&gt;&lt;/div&gt;
&lt;div style="width:20%;min-height:100px;background:#dccabe"&gt;&lt;/div&gt;
&lt;div style="width:20%;min-height:100px;background:#cdd4d4"&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;h3 id="how-do-the-curators-develop-each-gallery"&gt;How do the curators develop each gallery?&lt;/h3&gt;

&lt;p&gt;And what happens if multiple curators want the same piece for their gallery?&lt;/p&gt;

&lt;p&gt;Did they flip coins to see who would get to include a piece in their gallery? Is there a formal challenge like drawing an owl?&lt;/p&gt;

&lt;p&gt;If you know, please get in touch. :)&lt;/p&gt;

&lt;h2 id="people-watching-an-artist-shares-a-moment-with-someone-close"&gt;People Watching: An artist shares a moment with someone close&lt;/h2&gt;

&lt;p&gt;I witnessed an endearing moment wherein a woman photographed an older woman with a young fellow (barely 20) in front of his submission, a small photograph. The scene had the feel of a art teacher or mentor meeting her mentee to share the joy and pride of seeing the success of someone you care about. It was very touching to observe.&lt;/p&gt;

&lt;h2 id="so-much-fun-stuff"&gt;So Much Fun Stuff&lt;/h2&gt;

&lt;p&gt;I’ll stop writing here and leave you with a bunch of pictures of favorites.&lt;/p&gt;

&lt;div class="img-grid img-grid-2"&gt;

&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_160740_2023-07-13_10.51.24.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_160740_2023-07-13_10.51.24.jpg"&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;527: Daft vader, £155, Etching, Martin Langford, (Edition of 100 at £120)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_160642_2023-07-13_10.47.47.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_160642_2023-07-13_10.47.47.jpg"&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;650: Londinium plaza, £300, Screenprint, Frank Kiely, (Edition of 150 at £200)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161104_2023-07-13_10.52.45.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161104_2023-07-13_10.52.45.jpg"&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;858: Studio tiolet, left cubical, £495, Oil on board, A. Lincoln Taber&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161138_2023-07-13_10.52.52.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161138_2023-07-13_10.52.52.jpg"&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;856: Winter hyacinth, £2,800, Oil on gesso board, Ffiona Lewis&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;

&lt;div class="img-grid img-grid-2"&gt;
&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161228_2023-07-13_10.53.33.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161228_2023-07-13_10.53.33.jpg" alt="A photo of a wall mounted case of ceramic whistles all in the shape of animals."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;831: Moments from 365 days of Whistling 1, £10,000, Ceramic whistles, Emma Carlow&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161308_2023-07-13_10.53.44.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161308_2023-07-13_10.53.44.jpg" alt="Another photo of the ceramic whistles."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;More detail of 831&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161351_2023-07-13_10.53.53.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161351_2023-07-13_10.53.53.jpg" alt="Another photo of the ceramic whistles."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;More detail of 831&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161420_2023-07-13_10.54.01.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161420_2023-07-13_10.54.01.jpg" alt="Another photo of the ceramic whistles."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;More detail of 831&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;

&lt;figure class="img-large"&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161707_2023-07-13_10.56.50.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161707_2023-07-13_10.56.50.jpg" alt="A photo of a few paintings, including a small oil painting of a plastic drink lid crushed on the ground."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;809: Pick it up I, £600, Oil on board, Kate Wilson&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;div class="img-grid img-grid-3"&gt;
&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161759_2023-07-13_10.57.59.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161759_2023-07-13_10.57.59.jpg" alt="Another photo of multiple oil paintings on the gallery wall. Most of these are still life paintings of cups, vases, teapots and jars, some with vivid colors."&gt;
    &lt;/a&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161835_2023-07-13_10.59.03.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161835_2023-07-13_10.59.03.jpg" alt="Another photo of still life paintings, including one of an apartment door frame with a dozen different doorbells."&gt;
    &lt;/a&gt;
&lt;/figure&gt;
&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161535_2023-07-13_10.57.05.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161535_2023-07-13_10.57.05.jpg" alt="A painting of a dozen coffee mugs from above, in disarray."&gt;
    &lt;/a&gt;
&lt;/figure&gt;
&lt;/div&gt;

&lt;div class="img-grid img-grid-2"&gt;
&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161916_2023-07-13_11.05.50.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250610_161916_2023-07-13_11.05.50.jpg" alt="A painting of a dozen coffee mugs from above, in disarray."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;972: Held, £250, Screenprint, Alexandra Helm, (Edition of 30 at £170)&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_154408_2023-07-13_11.08.37.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_154408_2023-07-13_11.08.37.jpg" alt="A photo of a few paintings on a gallery wall."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;Theme: Animals&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;

&lt;div class="img-grid img-grid-2"&gt;
&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_154524_2023-07-13_11.09.00.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_154524_2023-07-13_11.09.00.jpg" alt="A photo of a gallery wall featuring an embroidery of a dog sitting on a couch."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;871: If only he could talk, NFS, Cotton and wool thread on linen and cotton, Christina MacDonald&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_214950_2023-07-13_11.16.55.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_214950_2023-07-13_11.16.55.jpg" alt="A photo of a gallery wall featuring a painting of two window washers on a modern glass building, hanging from ropes."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;1434: Together, £1,000, Oil on linen, Catherine Brereton. I like the window washers as subject.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;

&lt;div class="img-grid img-grid-2"&gt;
&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_154621_2023-07-13_11.14.10.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_154621_2023-07-13_11.14.10.jpg" alt="A photo of a few paintings on a gallery wall."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;Theme: interior spaces&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_215520_2023-07-13_11.20.19.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_215520_2023-07-13_11.20.19.jpg" alt="A photo of a few paintings on a gallery wall."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;Love these detailed views of city intersection construction. 1237: No stopping, £655, Acrylic, Matt Bannister. 1240: Push button and wait, £655, Acrylic, Matt Bannister.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;

&lt;figure class="img-medium"&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_215214_2023-07-13_11.17.20.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_215214_2023-07-13_11.17.20.jpg" alt="A photo of embroidery of an electrical outlet diagram."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;Embroidery of an electrical outlet diagram. 1441: Flip the switch, £400, Digital embroidery on felt, Irini Bachlitzanaki, (Edition of 20 at £300).&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure class="img-medium"&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_220158_2023-07-13_11.21.37.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_220158_2023-07-13_11.21.37.jpg" alt="A photo of a few paintings on a gallery wall."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;Highway vistas. 1266: From you to me, £595, Oil on cradled board, Jen Orpin. 1267: From A to B, £595, Oil on cradled board, Jen Orpin.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure class="img-medium img-center"&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_215737_2023-07-13_11.21.44.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_215737_2023-07-13_11.21.44.jpg" alt="A photo of a few paintings on a gallery wall. A wider view of the previous image."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;Theme: stripes and lines&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure class="img-medium"&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_215835_2023-07-13_11.23.27.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_215835_2023-07-13_11.23.27.jpg" alt="A photo of a gallery wall featuring a painting of the exterious of a library with two round windows behind trash bins and a bench."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;1304: Library, £2,250, Oil, James Judge&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;div class="img-grid img-grid-2"&gt;
&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_215748_2023-07-13_11.22.43.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_215748_2023-07-13_11.22.43.jpg" alt="A photo of a gallery wall featuring a painting of a figure in a dark outdoor environment who appears to be wearing a large owl mask, a cape with gold embellishments, and red leggings."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;1452: Daybreak communion, £950, Oil on board, Sam Forbes-Whitehead&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_215819_2023-07-13_11.23.13.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_215819_2023-07-13_11.23.13.jpg" alt="A photo of a gallery wall featuring a painting of a stairwell, looking down with angular shadows of the railings."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;1278: Tilt, £3,750, Acrylic and oil, Lindsey Lavender&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;/div&gt;

&lt;figure class="img-full"&gt;
    &lt;a href="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_215938_2023-07-13_11.32.29.jpg"&gt;
        &lt;img src="https://d3hgthf09mvo8y.cloudfront.net/blog-images/posts/2023-summer-exhibition/20250713_215938_2023-07-13_11.32.29.jpg" alt="A photo of two wall-mounted pieces and a resin sculpture on the floor in front."&gt;
    &lt;/a&gt;
    &lt;figcaption&gt;1512: Switch, £250, Marble and steel screw heads, Sharon Rankin, (Edition of 3 at £200).
    1513, Banana shoes, £550, Porcelain and underglaze, Poppy Houston.
    1583: Burnt out, £4,800, Resin, Helga Stentzel, (Edition of 5 at £4,800) (looks like a toaster made of bread).&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="submit"&gt;Submit&lt;/h2&gt;

&lt;p&gt;If you’re interested in submitting, go for it! Entry tickets go on sale in early January and the details of the work(s) must be submitted by early February: &lt;a href="http://www.roy.ac/submit"&gt;Call for Submissions&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="more-links"&gt;More Links&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://www.royalacademy.org.uk/exhibition/summer-exhibition-2023"&gt;Summer Exhibition 2023 Royal Academy of Arts&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.bbc.com/news/uk-england-london-65791955"&gt;Royal Academy Summer Exhibition: Who makes the cut?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://kultura.art/blogs/10/blogposts/188/the-price-of-art-at-the-summer-exhibition"&gt;KULTURA - The price of art at the Summer Exhibition from nicolita’s blog&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://register-of-charities.charitycommission.gov.uk/en/charity-search?p_p_id=uk_gov_ccew_onereg_charitydetails_web_portlet_CharityDetailsPortlet&amp;amp;p_p_lifecycle=2&amp;amp;p_p_state=maximized&amp;amp;p_p_mode=view&amp;amp;p_p_resource_id=%2Faccounts-resource&amp;amp;p_p_cacheability=cacheLevelPage&amp;amp;_uk_gov_ccew_onereg_charitydetails_web_portlet_CharityDetailsPortlet_objectiveId=A15327932&amp;amp;_uk_gov_ccew_onereg_charitydetails_web_portlet_CharityDetailsPortlet_priv_r_p_mvcRenderCommandName=%2Faccounts-and-annual-returns&amp;amp;_uk_gov_ccew_onereg_charitydetails_web_portlet_CharityDetailsPortlet_priv_r_p_organisationNumber=4040548"&gt;Charity Commission: Royal Academy of Arts: 2023 Accounts &amp;amp; Trustees’ Annual Report&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.royalacademy.org.uk/art-artists/archive/royal-academy-of-arts-official-archive"&gt;Royal Academy of Arts, building history&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://royal-academy-production-asset.s3.amazonaws.com/uploads/3f0769ae-c367-4bd6-8c9f-ffc75a34cee6/Summer%20Exhibition%202023%20(online).pdf"&gt;Summer Exhibition 2023 (online).pdf&lt;/a&gt; The List of Works, in case you want to look up something in a photo that I didn’t label in a caption.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20220110_google_signin/</id>
    <title>Google Sign-In Integration</title>
    <published>2022-01-10T00:00:00Z</published>
    <updated>2022-01-10T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20220110_google_signin/" type="text/html"/>
    <content type="html">&lt;h1 id="google-sign-in-integration"&gt;Google Sign-In Integration&lt;/h1&gt;

&lt;p&gt;One of my larger work projects at in 2021 was to implement Google Sign-In authentication as a method for signup (account creation) and login. This is an overview of the existing architecture and the changes and decisions made. I’m curious if you’ll have suggestions for alternative approaches.&lt;/p&gt;

&lt;h2 id="the-setting"&gt;The Setting&lt;/h2&gt;

&lt;p&gt;The business hypothesis was that signups would increase. But if you follow that logic through, the corollary is that people are giving up on signing up because they don’t want to create a password. That feels unlikely?? Regardless, we thought it would be nice and make our application look more professional and trustworthy. And doesn’t it, tho?&lt;/p&gt;

&lt;p&gt;I was already familiar with the current architecture for signup (having implemented self-serve signup) but for this project I needed to get familiar with our existing authentication methods (password, API token, Guest token, SAML, and PKI), and authorizations.&lt;/p&gt;

&lt;h2 id="architecture"&gt;Architecture&lt;/h2&gt;

&lt;p&gt;Note to the reader: The fact that I’m writing about this architecture does not necessarily mean it represents best practices. I’m open to comments and discussion. You may have or need a different architecture depending on your requirements.&lt;/p&gt;

&lt;h3 id="signup"&gt;Signup&lt;/h3&gt;

&lt;p&gt;When a visitor signs up, they provide a name, email, and password. Users must have a unique email address.&lt;/p&gt;

&lt;p&gt;The self-serve signup process creates an organization for the user as all user work must be associated with an organization.&lt;/p&gt;

&lt;p&gt;To join an existing organization, there are two invitation methods:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Email:&lt;/strong&gt; An organization admin sends an invitation to the user’s email. The user cannot change this email address. (Note: This is an important design decision and completely avoids a need to link existing accounts when an additional account is accidentally generated by the user.)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Invite URL:&lt;/strong&gt; A potential user visits a unique signup URL that was created for the organization. The user must provide an email address which matches a configured host domain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To prevent bot signups, a hidden Google Recaptcha creates a token which includes Google’s conclusion on whether the visitor is a bot. The token is added to the signup submission data and verified serverside.&lt;/p&gt;

&lt;p&gt;Because a user’s email address represents their identity, email verification is paramount and required before the user gains access to an account. Email invitees don’t need to have a verification email sent to them because they have already proven email ownership by following the invitation link emailed to them.&lt;/p&gt;

&lt;h3 id="login"&gt;Login&lt;/h3&gt;

&lt;p&gt;To log in, a user must provide the correct email and password combination. The login page posts the email and password to the session creation endpoint which authenticates the data and, if valid, fetches the user’s API token and additional account information. This information is encoded and stored in a session cookie.&lt;/p&gt;

&lt;p&gt;The authentication step, in addition to verifying identity, returns the user’s authorization, a definition of their access privileges such as &lt;code&gt;member&lt;/code&gt;, &lt;code&gt;owner&lt;/code&gt;, &lt;code&gt;admin&lt;/code&gt;, or, in the case of guests, &lt;code&gt;read_only&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id="information-privacy-measures"&gt;Information privacy measures&lt;/h3&gt;

&lt;p&gt;To avoid leaking information about whether an email address is already associated with an account, the signup process shows everyone the same success message to check for a verification email. In fact, a user who has an existing account will receive a different email letting them know that they tried to signup and already have an account.&lt;/p&gt;

&lt;p&gt;Similarly, when a visitor attempts to login, the same failure message is shown to everyone regardless of whether or not the email address is associated with an account.&lt;/p&gt;

&lt;h2 id="integration"&gt;Integration&lt;/h2&gt;

&lt;p&gt;Since I’d never worked on authentication before, I was really starting with some basic questions. How do we know someone is who they say they are? Who can we trust?&lt;/p&gt;

&lt;p&gt;With Google Sign-In, the client application is asking Google to authenticate the user. Does this user have an account with Google? Are they currently authenticated with Google? If not, we’re happy to wait while they authenticate with Google right now.&lt;/p&gt;

&lt;h3 id="how-it-works"&gt;How it works&lt;/h3&gt;

&lt;p&gt;The clientside application must initialize the Google Sign-In JS with our organization’s client ID. This script can be used to generate a “Sign in with Google” button that can be placed where desired.&lt;/p&gt;

&lt;p&gt;When the user clicks on the “Sign in with Google” button, a new browser window opens where the user is asked to select a Google account that they are already logged into or allowed to log into a different account. Once they have selected and authenticated an account, they are shown a consent screen declaring the content that Google will share with your organization (in this case, just profile info like name and email).&lt;/p&gt;

&lt;p&gt;When the user confirms, the window is closed and the clientside Google Sign-In script executes a callback with a JSON Web Token (JWT) as an argument. The JWT may be decoded (there are third-party libraries for decoding) to access data about the user.&lt;/p&gt;

&lt;p&gt;The JWT should be sent, still encoded, to your server to complete the account creation. A serverside Google Sign-In library is very useful to decode and verify the JWT.&lt;/p&gt;

&lt;p&gt;The process is the same for login. There are options in the clientside JS to select the label and styles of the Google Sign-In button.&lt;/p&gt;

&lt;h2 id="next-time"&gt;Next Time&lt;/h2&gt;

&lt;p&gt;What would I do differently next time?&lt;/p&gt;

&lt;p&gt;Well, I didn’t give alternatives a serious thought. auth0, okta? Would these be easier? If you have an opinion, I’d love to hear it. I liked working with the source, Google itself, rather than an intermediary, to learn about Google Sign-In.&lt;/p&gt;

&lt;p&gt;I knew I didn’t care about adding other signup methods, like Facebook or Github, which would be the advantage of these other solutions.&lt;/p&gt;

&lt;p&gt;I also didn’t need to look into handling Google authorizations beyond user profile information. Connecting a user’s Google Calendar or Google Docs data is an additional set of concerns.&lt;/p&gt;

&lt;h2 id="ps-dear-product-team"&gt;PS: Dear Product Team&lt;/h2&gt;

&lt;p&gt;One of the improvements I think could be made to Google Sign-In, as a user, would be some indication of which account I had signed in with before, if at all.&lt;/p&gt;

&lt;p&gt;I’m usually authenticated in 3 different Google accounts at a time and I have many more.&lt;/p&gt;

&lt;p&gt;When asked to authenticate with Google, I forget which Google account I used to login and accidentally create a new account (depending on the implementation of said app).&lt;/p&gt;

&lt;p&gt;This is the main reason I don’t use Google Sign-In unless required. I don’t like accidentally creating multiple accounts. What’s an extra account? It’s not like the stack of loyalty coffee cards that stretch out my wallet which in turn wears a hole in my jean pocket.&lt;/p&gt;

&lt;p&gt;To deal with this, this little pet peeve, I create 1Password logins for sites even when I use Google Sign-In so that I can leave a note to myself about which Google account I used. Dear 1Password product team…&lt;/p&gt;

&lt;h2 id="resources"&gt;Resources&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;If you’re doing a Google Sign-In integration, start with &lt;a href="https://developers.google.com/identity/gsi/web/guides/overview"&gt;the official docs&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://cloud.google.com/nodejs/docs/reference/google-auth-library/latest"&gt;Documentation: Google Auth Library: Node.js Client&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s an &lt;a href="https://developers.google.com/identity/gsi/web/reference/js-reference#CredentialResponse"&gt;example JWT&lt;/a&gt; from the docs:&lt;/p&gt;

&lt;div class="language-js highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20220110_google_signin/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;// header&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20220110_google_signin/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;{
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20220110_google_signin/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;alg&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;RS256&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;,
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20220110_google_signin/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;kid&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;f05415b13acb9590f70df862765c655f5a7a019e&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span style="color:#777"&gt;// JWT signature&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20220110_google_signin/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;typ&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;JWT&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20220110_google_signin/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;}
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20220110_google_signin/#n7" name="n7"&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;// payload&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20220110_google_signin/#n8" name="n8"&gt;8&lt;/a&gt;&lt;/span&gt;{
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20220110_google_signin/#n9" name="n9"&gt;9&lt;/a&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;iss&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;[https://accounts.google.com](https://accounts.google.com/)&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span style="color:#777"&gt;// The JWT's issuer&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20220110_google_signin/#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;nbf&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;:  &lt;span style="color:#00D"&gt;161803398874&lt;/span&gt;,
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20220110_google_signin/#n11" name="n11"&gt;11&lt;/a&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;aud&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;[314159265-pi.apps.googleusercontent.com](http://314159265-pi.apps.googleusercontent.com/)&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span style="color:#777"&gt;// Your server's client ID&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20220110_google_signin/#n12" name="n12"&gt;12&lt;/a&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;sub&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;3141592653589793238&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span style="color:#777"&gt;// The unique ID of the user's Google Account&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20220110_google_signin/#n13" name="n13"&gt;13&lt;/a&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;hd&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;[gmail.com](http://gmail.com/)&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span style="color:#777"&gt;// If present, the host domain of the user's GSuite email address&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20220110_google_signin/#n14" name="n14"&gt;14&lt;/a&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;[elisa.g.beckett@gmail.com](mailto:elisa.g.beckett@gmail.com)&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;, &lt;span style="color:#777"&gt;// The user's email address&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20220110_google_signin/#n15" name="n15"&gt;15&lt;/a&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;email_verified&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span style="color:#069"&gt;true&lt;/span&gt;, &lt;span style="color:#777"&gt;// true, if Google has verified the email address&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20220110_google_signin/#n16" name="n16"&gt;16&lt;/a&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;azp&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;[314159265-pi.apps.googleusercontent.com](http://314159265-pi.apps.googleusercontent.com/)&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;,
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20220110_google_signin/#n17" name="n17"&gt;17&lt;/a&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;Elisa Beckett&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;,
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20220110_google_signin/#n18" name="n18"&gt;18&lt;/a&gt;&lt;/span&gt;  &lt;span style="color:#777"&gt;// If present, a URL to user's profile picture&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20220110_google_signin/#n19" name="n19"&gt;19&lt;/a&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;picture&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;[https://lh3.googleusercontent.com/a-/e2718281828459045235360uler](https://lh3.googleusercontent.com/a-/e2718281828459045235360uler)&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;,
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20220110_google_signin/#n20" name="n20"&gt;20&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;given_name&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;Elisa&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;,
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20220110_google_signin/#n21" name="n21"&gt;21&lt;/a&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;family_name&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;Beckett&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;,
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20220110_google_signin/#n22" name="n22"&gt;22&lt;/a&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;iat&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span style="color:#00D"&gt;1596474000&lt;/span&gt;, &lt;span style="color:#777"&gt;// Unix timestamp of the assertion's creation time&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20220110_google_signin/#n23" name="n23"&gt;23&lt;/a&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;exp&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span style="color:#00D"&gt;1596477600&lt;/span&gt;, &lt;span style="color:#777"&gt;// Unix timestamp of the assertion's expiration time&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20220110_google_signin/#n24" name="n24"&gt;24&lt;/a&gt;&lt;/span&gt;  &lt;span style="color:#606"&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;span&gt;jti&lt;/span&gt;&lt;span style="color:#404"&gt;"&lt;/span&gt;&lt;/span&gt;: &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;abc161803398874def&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20220110_google_signin/#n25" name="n25"&gt;25&lt;/a&gt;&lt;/span&gt;}
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20181129_remainders/</id>
    <title>Leftovers: What's the difference between Clojure's rem(ainder) and mod(ulus) functions?</title>
    <published>2018-11-29T00:00:00Z</published>
    <updated>2018-11-29T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20181129_remainders/" type="text/html"/>
    <content type="html">&lt;h1 id="leftovers-whats-the-difference-between-clojures-remainder-and-modulus-functions"&gt;Leftovers: What’s the difference between Clojure’s rem(ainder) and mod(ulus) functions?&lt;/h1&gt;

&lt;p&gt;Clojure has two functions for finding the remainder in integer division: &lt;code&gt;rem&lt;/code&gt; and &lt;code&gt;mod&lt;/code&gt;. Turns out, many other languages also have remainder and modulus functions.&lt;/p&gt;

&lt;p&gt;Why? What the heck? What’s the difference?&lt;/p&gt;

&lt;p&gt;At first blush, these functions seem the same:&lt;/p&gt;

&lt;div class="language-clojure highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;5&lt;/span&gt; &lt;span style="color:#00D"&gt;3&lt;/span&gt;)
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;2&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;5&lt;/span&gt; &lt;span style="color:#00D"&gt;3&lt;/span&gt;)
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;2&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n7" name="n7"&gt;7&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;-5&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n8" name="n8"&gt;8&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;-2&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n9" name="n9"&gt;9&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;-5&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n11" name="n11"&gt;11&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;-2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;This sameness only holds when the arguments are &lt;strong&gt;both positive&lt;/strong&gt; or &lt;strong&gt;both negative.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check this out:&lt;/p&gt;

&lt;div class="language-clojure highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; negative numerator, positive divisor&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;-5&lt;/span&gt; &lt;span style="color:#00D"&gt;3&lt;/span&gt;)
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;-2&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;-5&lt;/span&gt; &lt;span style="color:#00D"&gt;3&lt;/span&gt;)
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;1&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n7" name="n7"&gt;7&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n8" name="n8"&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; positive numerator, negative divisor&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n9" name="n9"&gt;9&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;5&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;2&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n11" name="n11"&gt;11&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n12" name="n12"&gt;12&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;5&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n13" name="n13"&gt;13&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;-1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;If one of the arguments is negative, we get different results from &lt;code&gt;rem&lt;/code&gt; and &lt;code&gt;mod&lt;/code&gt;.
Not only that, we get different results &lt;em&gt;within&lt;/em&gt; &lt;code&gt;rem&lt;/code&gt; and &lt;code&gt;mod&lt;/code&gt; depending on whether its the numerator or divisor that is negative.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why are there two functions? Why isn’t there just one way to calculate a remainder like I learned in school? When do you want to use one over the other?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I thought these questions would be easy to answer. There are certainly a lot of answers but not many were complete.&lt;/p&gt;

&lt;h2 id="the-first-level-answers"&gt;The first level answers&lt;/h2&gt;

&lt;p&gt;These are the two most common and useful answers I found:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code&gt;mod&lt;/code&gt; is different because it returns a remainder that reflects the sign of the divisor.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code&gt;mod&lt;/code&gt; floors the quotient toward negative infinity while &lt;code&gt;rem&lt;/code&gt; truncates the quotient toward zero.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first explanation is true and it answers my second question, “When do you want to use &lt;code&gt;rem&lt;/code&gt; over &lt;code&gt;mod&lt;/code&gt;?” by saying, “Use &lt;code&gt;mod&lt;/code&gt; when you want the remainder to have the same sign as the divisor.”&lt;/p&gt;

&lt;p&gt;This explanation implies that the reason there are two different functions is because you might want the results to be different in these certain cases.&lt;/p&gt;

&lt;p&gt;As far as explanations go, it falls flat. What’s the motivation? Humans made this! What were they thinking? Is there a Hollywood heist movie behind this - computer programmer replaces &lt;code&gt;rem&lt;/code&gt; with &lt;code&gt;mod&lt;/code&gt; to siphon off remainders of the stock market?&lt;/p&gt;

&lt;p&gt;I disqualify the first explanation because it doesn’t satisfy my “why” questions.&lt;/p&gt;

&lt;p&gt;The second explanation provides a little more information about the decision (flooring vs truncating) that results in the first explanation.&lt;/p&gt;

&lt;p&gt;I’m still not satisfied with “why” we have two ways to calculate the remainder but first, I want to take a quick detour into integer division and the concepts of flooring and truncating.&lt;/p&gt;

&lt;h2 id="integer-division-whats-happening-inside-rem-and-mod"&gt;Integer division: what’s happening inside &lt;code&gt;rem&lt;/code&gt; and &lt;code&gt;mod&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;When I first learned about &lt;code&gt;rem&lt;/code&gt; and &lt;code&gt;mod&lt;/code&gt;, I was surprised that there are multiple ways to calculate a remainder.&lt;/p&gt;

&lt;p&gt;I am much more surprised that I was surprised. I have a math degree, shouldn’t I know this? I spent years in elementary school carrying out division and manipulating fractions - why didn’t I notice then? Did I forget that I knew this once? I really don’t know.&lt;/p&gt;

&lt;p&gt;Are &lt;code&gt;rem&lt;/code&gt; and &lt;code&gt;mod&lt;/code&gt; based on the the division algorithms we learn in school? What did we learn in school?&lt;/p&gt;

&lt;p&gt;I learned a simplified version of integer division.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The convention is to return the value of x that satisfies the congruence and lies between 0 and m. (&lt;a href="http://mathcentral.uregina.ca/QQ/database/QQ.09.12/h/eric1.html"&gt;source&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="language-clojure highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; division: -5/3 = 5/-3 -1.66&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; - / + negative numerator, positive divisor&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; integer division: -5 = (3 * -1) + (-2)&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;-5&lt;/span&gt; &lt;span style="color:#00D"&gt;3&lt;/span&gt;)
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n7" name="n7"&gt;7&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;-2&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n8" name="n8"&gt;8&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n9" name="n9"&gt;9&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; integer division: -5 = (3 * -2) + 1&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; -2 is closer to negative infinity than the -1 quotient provided by `rem`&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n11" name="n11"&gt;11&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;-5&lt;/span&gt; &lt;span style="color:#00D"&gt;3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n12" name="n12"&gt;12&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;1&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n13" name="n13"&gt;13&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n14" name="n14"&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; + / - positive numerator, negative divisor&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n15" name="n15"&gt;15&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n16" name="n16"&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; integer division: 5 = (-3 * -1) + 2&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n17" name="n17"&gt;17&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;5&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n18" name="n18"&gt;18&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;2&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n19" name="n19"&gt;19&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n20" name="n20"&gt;20&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; integer division: 5 = (-3 * -2) + (-1)&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n21" name="n21"&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; -2 is closer to negative infinity than the -1 quotient provided by `rem`&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n22" name="n22"&gt;22&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;5&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n23" name="n23"&gt;23&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;-1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;h2 id="use-case-bucketing-values"&gt;Use case: bucketing values&lt;/h2&gt;

&lt;p&gt;Remainders are a way for us to identify groups of numbers.&lt;/p&gt;

&lt;div class="language-clojure highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;3&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;0&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;2&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;-1&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n7" name="n7"&gt;7&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;1&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n8" name="n8"&gt;8&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;-2&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n9" name="n9"&gt;9&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;0&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n11" name="n11"&gt;11&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;0&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n12" name="n12"&gt;12&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n13" name="n13"&gt;13&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;-1&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n14" name="n14"&gt;14&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;-1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;You can see a pattern emerge in the remainders. Because we’re dividing by -3, our remainder can only ever be between 0 and -3. We can group {0, 3} together because they both have remainder 0 when divided by -3, same with {-1, 2}.&lt;/p&gt;

&lt;p&gt;Doesn’t &lt;code&gt;rem&lt;/code&gt; do the same thing?&lt;/p&gt;

&lt;div class="language-clojure highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;3&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;0&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;2&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;2&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n7" name="n7"&gt;7&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;1&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n8" name="n8"&gt;8&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;1&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n9" name="n9"&gt;9&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;0&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n11" name="n11"&gt;11&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;0&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n12" name="n12"&gt;12&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n13" name="n13"&gt;13&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;-1&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n14" name="n14"&gt;14&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;-1&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n15" name="n15"&gt;15&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n16" name="n16"&gt;16&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;-2&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n17" name="n17"&gt;17&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;-2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Nope! Because &lt;code&gt;rem&lt;/code&gt; is flooring to zero, it starts a new cycle of remainders when it passes 0. -1 and 2 are no longer in the same group.&lt;/p&gt;

&lt;p&gt;Why would we want to group numbers based on their remainders?&lt;/p&gt;

&lt;h2 id="use-case-limiting-a-function-range"&gt;Use case: limiting a function range&lt;/h2&gt;

&lt;p&gt;This morning, I happened across this basic Clojure exercise:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Using REPL documentation functions, find the documentation for the rem and mod functions. Compare the results of the provided expressions based on the documentation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;from: &lt;a href="https://clojure.org/guides/learn/syntax"&gt;https://clojure.org/guides/learn/syntax&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rem&lt;/code&gt; is “…the integer “left over” after dividing one integer by another to produce an integer quotient (integer division).”&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mod&lt;/code&gt; stands for modulus or modulo operation which is defined as “… the operation that produces such a remainder when given a dividend and divisor.”&lt;/p&gt;

&lt;p&gt;Sounds like &lt;code&gt;rem&lt;/code&gt; and &lt;code&gt;mod&lt;/code&gt; do the same thing, so what’s the difference?&lt;/p&gt;

&lt;p&gt;Turns out this question is asked a lot in programming because functions for remainder and modulus vary between language. Language designers had to make a choice about how to implement these mathematical concepts.&lt;/p&gt;

&lt;p&gt;I don’t remember learning multiple ways to calculate a remainder in school so I did some digging. The following is a summary of what I learned. I tried to keep it easy-to-understand and memorable so that I won’t forget it a second time.&lt;/p&gt;

&lt;p&gt;You can skip the refresher on integer division if you just want to know what the difference is between &lt;code&gt;rem&lt;/code&gt; and &lt;code&gt;mod&lt;/code&gt; in Clojure.&lt;/p&gt;

&lt;p&gt;My main source of understanding came from &lt;a href="https://blogs.msdn.microsoft.com/ericlippert/2011/12/05/whats-the-difference-remainder-vs-modulus/"&gt;an excellent run down by Eric Lippert on the Microsoft Developer blog from 2011.&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="why-do-we-divide-things"&gt;Why do we divide things?&lt;/h2&gt;

&lt;p&gt;Division is dividing something up into equal parts. 10 pies divided amongst 4 people means each person can have 2.5 pies. That’s great. We’re all happy.&lt;/p&gt;

&lt;p&gt;Sometimes the objects we’re dividing aren’t useful when we cut them into parts. Half a pie is still tasty but half an iPhone is not usable.&lt;/p&gt;

&lt;p&gt;If we have 5 iPhones and 3 people, how many iPhones can each person have so that each person has the same number of iPhones?&lt;/p&gt;

&lt;p&gt;Remember, we want to make sure each person has an equal amount, to avoid fighting.&lt;/p&gt;

&lt;p&gt;When we have more objects than people, then we have leftover iPhones, a remainder.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;5 / 3 = 1 iPhone per person, with 2 leftover iPhones.&lt;/p&gt;

  &lt;p&gt;5 / 3 = 1, remainder 2.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can flip this equation around to say:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;(1 iPhone per person * 3 people) + 2 leftover iPhones = 5 iPhones.&lt;/p&gt;

  &lt;p&gt;(1 * 3) + 2 = 5&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Following this algorithm, we can generate other valid solutions.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;(2 iPhones per person * 3 people) + 1 missing iPhone = 5 iPhones&lt;/p&gt;

  &lt;p&gt;(2 * 3) + (-1) = 5&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;(-1 iPhones per person * -3 people) + 2 iPhones = 5 iPhones&lt;/p&gt;

  &lt;p&gt;(-1 * -3) + 2 = 5&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id="negative-remainders"&gt;Negative remainders&lt;/h2&gt;

&lt;p&gt;When we’re out in the world dividing up pies and iPhones, the leftover, or remainder, is always positive. We don’t have negative pies or iPhones, or do we?&lt;/p&gt;

&lt;p&gt;Wouldn’t it also be true to say, that each person could have 2 iPhones but one person would have to give up one of their two iPhones?&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;(2 iPhones per person * 3 people) + (-1 iPhone) = 5 iPhones.&lt;/p&gt;

  &lt;p&gt;(2 * 3) + (-1) = 5&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;5 iPhones divided by 3 people = 2 iPhones per person, with one iPhone returned.&lt;/p&gt;

  &lt;p&gt;5 / 3 = 2, remainder -1.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is conceptually possible (being short an iPhone) and mathematically correct.&lt;/p&gt;

&lt;p&gt;I don’t know about you, but I’d prefer to tell my friends they can each have one iPhone and hide the extras rather than tell them they are entitled to two as long as we agree that someone won’t get their second.&lt;/p&gt;

&lt;p&gt;There are endless other solutions that are less useful. For example, 5 / 3 = 20, remainder -55. I give each of you 20 iPhones but altogether you’ve got to give me 55 of them back? It works on paper but…&lt;/p&gt;

&lt;h2 id="choosing-a-solution"&gt;Choosing a Solution&lt;/h2&gt;

&lt;p&gt;I’ve been talking about iPhones because I want to convince you that there are many possible solutions. We don’t think about it much because we have a preference for certain solutions over others.&lt;/p&gt;

&lt;p&gt;How do you choose a solution?&lt;/p&gt;

&lt;p&gt;In the above scenario, my preferred remainder is:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;positive (To maintain peace and keep things simple.)&lt;/li&gt;
  &lt;li&gt;small (I can’t afford 60 iPhones even if I’m going to return 55.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matches what mathematicians use to define integer division.&lt;/p&gt;

&lt;p&gt;The answer should be the &lt;strong&gt;least positive remainder.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id="what-about-negative-numbers"&gt;What about negative numbers?&lt;/h2&gt;

&lt;p&gt;We can consider a negative iPhone is an iPhone owed, lent, or given away.&lt;/p&gt;

&lt;p&gt;Imagine we can construct a mega iPhone from 5 iPhones. I’ll call it a MEGAPhone. I need convince my friends to lend me their iPhones so I can create a MEGAPhone.&lt;/p&gt;

&lt;p&gt;We could have:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;1) -5 / 3 = -1, remainder -2&lt;/p&gt;

  &lt;p&gt;3 people contribute 1 iPhone each and we’re still 2 iPhones short for our MEGAPhone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or maybe…&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;2) -5 / 3 = -2, remainder 1&lt;/p&gt;

  &lt;p&gt;3 people contribute 2 iPhones each for a total of 6 loaned iPhones, which is 1 iPhone more than we needed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What if we flip the signs of our numbers?&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;3) 5 / -3 = -1, remainder 2&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;4) 5 / -3 = -2, remainder -1&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Frankly, I don’t know how to explain what the above equations mean in real-world terms. We’ll come to a better example of a negative divisor in minute.&lt;/p&gt;

&lt;h2 id="choosing-a-solution-again"&gt;Choosing a Solution Again&lt;/h2&gt;

&lt;p&gt;Which solution should I choose?&lt;/p&gt;

&lt;p&gt;In this example, I’d rather not be short an iPhone because the MEGAPhone isn’t a MEGAPhone with just 4 loaned iPhones.&lt;/p&gt;

&lt;p&gt;That means I use either&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;2) -5 / 3 = -2, remainder 1&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;4) 5 / -3 = -2, remainder -1&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But imagine I can only get 1 iPhone from each friend. Then I need to know, “How many iPhones do I need to provide personally?” In that case, I want to know that I am 2 iPhones short, remainder -2.&lt;/p&gt;

&lt;p&gt;If we go back to the mathematicians, they further define integer division as returning the least &lt;strong&gt;absolute&lt;/strong&gt; remainder.&lt;/p&gt;

&lt;p&gt;The mathematicians prefer that I ask my friends for 2 iPhones each so that I have one extra.&lt;/p&gt;

&lt;h2 id="why-doesnt-this-sound-like-what-i-was-taught-in-school"&gt;Why doesn’t this sound like what I was taught in school?&lt;/h2&gt;

&lt;p&gt;To be honest, all I remember is carrying out long division on positive integers and having a remainder.&lt;/p&gt;

&lt;p&gt;I can’t recall anything about finding the &lt;strong&gt;least absolute remainder.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Do you remember learning that there are multiple ways to divide a number when you can play with the remainder? Or that you should take the absolute value of your remainder?&lt;/p&gt;

&lt;p&gt;Let me know, I’m curious what you remember from school.&lt;/p&gt;

&lt;h2 id="the-difference-between-rem-and-mod-in-clojure"&gt;The difference between &lt;code&gt;rem&lt;/code&gt; and &lt;code&gt;mod&lt;/code&gt; in Clojure&lt;/h2&gt;

&lt;p&gt;I’ll run a couple experiments to show the difference before I talk about it.&lt;/p&gt;

&lt;div class="language-clojure highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; positive numerator, positive divisor&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;5&lt;/span&gt; &lt;span style="color:#00D"&gt;3&lt;/span&gt;)
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;2&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;5&lt;/span&gt; &lt;span style="color:#00D"&gt;3&lt;/span&gt;)
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;2&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n7" name="n7"&gt;7&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n8" name="n8"&gt;8&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; negative numerator, positive divisor&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n9" name="n9"&gt;9&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;-5&lt;/span&gt; &lt;span style="color:#00D"&gt;3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;-2&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n11" name="n11"&gt;11&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n12" name="n12"&gt;12&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;-5&lt;/span&gt; &lt;span style="color:#00D"&gt;3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n13" name="n13"&gt;13&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;1&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n14" name="n14"&gt;14&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n15" name="n15"&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; positive numerator, negative divisor&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n16" name="n16"&gt;16&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;5&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n17" name="n17"&gt;17&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;2&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n18" name="n18"&gt;18&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n19" name="n19"&gt;19&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;5&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n20" name="n20"&gt;20&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;-1&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n21" name="n21"&gt;21&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n22" name="n22"&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; negative numerator, negative divisor&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n23" name="n23"&gt;23&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;-5&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n24" name="n24"&gt;24&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;-2&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n25" name="n25"&gt;25&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n26" name="n26"&gt;26&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;-5&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n27" name="n27"&gt;27&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;-2&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n28" name="n28"&gt;28&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n29" name="n29"&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; also note the zero cases&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n30" name="n30"&gt;30&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;-5&lt;/span&gt; &lt;span style="color:#00D"&gt;0&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n31" name="n31"&gt;31&lt;/a&gt;&lt;/span&gt;=&amp;gt; java.lang.ArithmeticException&lt;span style="color:#F00;background-color:#FAA"&gt;:&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;/&lt;/span&gt; by zero
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n32" name="n32"&gt;32&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n33" name="n33"&gt;33&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;-5&lt;/span&gt; &lt;span style="color:#00D"&gt;0&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n34" name="n34"&gt;34&lt;/a&gt;&lt;/span&gt;=&amp;gt; java.lang.ArithmeticException&lt;span style="color:#F00;background-color:#FAA"&gt;:&lt;/span&gt; Divide by zero
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n35" name="n35"&gt;35&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n36" name="n36"&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; I'm not sure yet why one error uses `/` and the other uses `Divide`, feel free to email me if you have an idea or the answer.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;code&gt;rem&lt;/code&gt; and &lt;code&gt;mod&lt;/code&gt; act the same when the numerator and divisor are the same sign (negative or positive).&lt;/p&gt;

&lt;p&gt;Why do they act differently when the numerator and divisor have opposing signs?&lt;/p&gt;

&lt;p&gt;Let’s investigate the source code.&lt;/p&gt;

&lt;div class="language-clojure highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;(source &lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt;)
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;defn&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;rem&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;  &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;remainder of dividing numerator by denominator.&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;  {&lt;span style="color:#A60"&gt;:added&lt;/span&gt; &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;1.0&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;   &lt;span style="color:#A60"&gt;:static&lt;/span&gt; &lt;span style="color:#069"&gt;true&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;   &lt;span style="color:#A60"&gt;:inline&lt;/span&gt; (&lt;span style="color:#080;font-weight:bold"&gt;fn&lt;/span&gt; [x y] `(&lt;span style="color:#080;font-weight:bold"&gt;.&lt;/span&gt; clojure.lang.Numbers (remainder ~x ~y)))}
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n7" name="n7"&gt;7&lt;/a&gt;&lt;/span&gt;  [&lt;span style="color:#080;font-weight:bold"&gt;num&lt;/span&gt; div]
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n8" name="n8"&gt;8&lt;/a&gt;&lt;/span&gt;    (&lt;span style="color:#080;font-weight:bold"&gt;.&lt;/span&gt; clojure.lang.Numbers (remainder &lt;span style="color:#080;font-weight:bold"&gt;num&lt;/span&gt; div)))
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n9" name="n9"&gt;9&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#069"&gt;nil&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n11" name="n11"&gt;11&lt;/a&gt;&lt;/span&gt;(source &lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n12" name="n12"&gt;12&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;defn&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;mod&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n13" name="n13"&gt;13&lt;/a&gt;&lt;/span&gt;  &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;Modulus of num and div. Truncates toward negative infinity.&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n14" name="n14"&gt;14&lt;/a&gt;&lt;/span&gt;  {&lt;span style="color:#A60"&gt;:added&lt;/span&gt; &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;1.0&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n15" name="n15"&gt;15&lt;/a&gt;&lt;/span&gt;   &lt;span style="color:#A60"&gt;:static&lt;/span&gt; &lt;span style="color:#069"&gt;true&lt;/span&gt;}
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n16" name="n16"&gt;16&lt;/a&gt;&lt;/span&gt;  [&lt;span style="color:#080;font-weight:bold"&gt;num&lt;/span&gt; div]
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n17" name="n17"&gt;17&lt;/a&gt;&lt;/span&gt;  (&lt;span style="color:#080;font-weight:bold"&gt;let&lt;/span&gt; [m (&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;num&lt;/span&gt; div)]
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n18" name="n18"&gt;18&lt;/a&gt;&lt;/span&gt;    (&lt;span style="color:#080;font-weight:bold"&gt;if&lt;/span&gt; (&lt;span style="color:#080;font-weight:bold"&gt;or&lt;/span&gt; (&lt;span style="color:#080;font-weight:bold"&gt;zero?&lt;/span&gt; m) (&lt;span style="color:#080;font-weight:bold"&gt;=&lt;/span&gt; (&lt;span style="color:#080;font-weight:bold"&gt;pos?&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;num&lt;/span&gt;) (&lt;span style="color:#080;font-weight:bold"&gt;pos?&lt;/span&gt; div)))
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n19" name="n19"&gt;19&lt;/a&gt;&lt;/span&gt;      m
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n20" name="n20"&gt;20&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;      (&lt;span style="color:#080;font-weight:bold"&gt;+&lt;/span&gt; m div))))
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n21" name="n21"&gt;21&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#069"&gt;nil&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Discussing the source of &lt;code&gt;rem&lt;/code&gt; is beyond the scope of this article. The implementation of &lt;code&gt;rem&lt;/code&gt; was set first, and &lt;code&gt;mod&lt;/code&gt; was created to offer a different choice in case the definition of &lt;code&gt;rem&lt;/code&gt; doesn’t suit your purposes.&lt;/p&gt;

&lt;p&gt;Let’s look at how &lt;code&gt;mod&lt;/code&gt; utilizes &lt;code&gt;rem&lt;/code&gt; and what creates the difference.&lt;/p&gt;

&lt;div class="language-clojure highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;let&lt;/span&gt; [m (&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;num&lt;/span&gt; div)]
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;    (&lt;span style="color:#080;font-weight:bold"&gt;if&lt;/span&gt; (&lt;span style="color:#080;font-weight:bold"&gt;or&lt;/span&gt; (&lt;span style="color:#080;font-weight:bold"&gt;zero?&lt;/span&gt; m) (&lt;span style="color:#080;font-weight:bold"&gt;=&lt;/span&gt; (&lt;span style="color:#080;font-weight:bold"&gt;pos?&lt;/span&gt; &lt;span style="color:#080;font-weight:bold"&gt;num&lt;/span&gt;) (&lt;span style="color:#080;font-weight:bold"&gt;pos?&lt;/span&gt; div)))
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;      m
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;      (&lt;span style="color:#080;font-weight:bold"&gt;+&lt;/span&gt; m div))))
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;code&gt;mod&lt;/code&gt; considers three cases. Here are the first two:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;the remainder is 0 =&amp;gt; just return 0, end of story&lt;/li&gt;
  &lt;li&gt;the numerator and divisor are the same sign (either both positive or both negative) =&amp;gt; return the remainder; act just like &lt;code&gt;rem&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thirdly, the else case. After eliminating the first two cases, we have a non-zero remainder and the numerator and divisor have opposing signs.&lt;/p&gt;

&lt;p&gt;In this else case, &lt;code&gt;(+ m div)&lt;/code&gt;, add the divisor to the remainder.&lt;/p&gt;

&lt;div class="language-clojure highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; negative numerator, positive divisor&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;-5&lt;/span&gt; &lt;span style="color:#00D"&gt;3&lt;/span&gt;)
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;-2&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;-5&lt;/span&gt; &lt;span style="color:#00D"&gt;3&lt;/span&gt;)
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; (+ (rem -5 3) 3)&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n7" name="n7"&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; (+ -2 3)&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n8" name="n8"&gt;8&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;1&lt;/span&gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20181129_remainders/#n9" name="n9"&gt;9&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; positive numerator, negative divisor&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n11" name="n11"&gt;11&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;rem&lt;/span&gt; &lt;span style="color:#00D"&gt;5&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n12" name="n12"&gt;12&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;2&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n13" name="n13"&gt;13&lt;/a&gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n14" name="n14"&gt;14&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;mod&lt;/span&gt; &lt;span style="color:#00D"&gt;5&lt;/span&gt; &lt;span style="color:#00D"&gt;-3&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n15" name="n15"&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; (+ (rem 5 -3) -3)&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n16" name="n16"&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#777"&gt;;; (+ 2 -3)&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20181129_remainders/#n17" name="n17"&gt;17&lt;/a&gt;&lt;/span&gt;=&amp;gt; &lt;span style="color:#00D"&gt;-1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;As a result, &lt;code&gt;mod&lt;/code&gt; gives us a least absolute remainder.&lt;/p&gt;

&lt;p&gt;I actually don’t have a nice way to wrap this up. From what I can tell, &lt;code&gt;mod&lt;/code&gt; aligns with Wikipedia and Wolfram Alpha, which must be the indisputably true way.&lt;/p&gt;

&lt;p&gt;So, how did &lt;code&gt;rem&lt;/code&gt; originate? Why can &lt;code&gt;mod&lt;/code&gt; rely on this simple bit of addition to create a least absolute remainder from &lt;code&gt;rem&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Stay tuned.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20180716_stopslacking-multipart/</id>
    <title>A multipart mishap: adding plain text email support to Stop Slacking</title>
    <published>2018-07-23T00:00:00Z</published>
    <updated>2018-07-23T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20180716_stopslacking-multipart/" type="text/html"/>
    <content type="html">&lt;h1 id="a-multipart-mishap-br-adding-plain-text-email-support-to-stop-slacking"&gt;A Multipart Mishap: &lt;br&gt; Adding plain text email support to Stop Slacking&lt;/h1&gt;

&lt;div class="info"&gt;
  &lt;div class="info-header"&gt;
    &lt;p class="info-title"&gt;Update&lt;/p&gt;
    &lt;p class="info-date"&gt;December 21, 2025&lt;/p&gt;
  &lt;/div&gt;
  &lt;p&gt;This project has been retired. After a couple years, Slack finally introduced this feature. :)&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Recently, I discovered that &lt;a href="https://www.stopslacking.com/"&gt;Stop Slacking,&lt;/a&gt; a free reply-by-email service I operate, wasn’t processing plain text emails. This is a story about troubleshooting with a product design footnote.&lt;/p&gt;

&lt;h2 id="step-0-never-ignore-your-error-messages"&gt;Step 0: Never ignore your error messages&lt;/h2&gt;

&lt;p&gt;A user let me know that nothing happened when they tried to subscribe. Their subscription email looked fine so I reviewed my error message logs for another clue.&lt;/p&gt;

&lt;p&gt;For weeks, I had been receiving error messages that said:&lt;/p&gt;

&lt;div class="language-text highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;java.lang.IllegalArgumentException: No matching method found: getBodyPart for class java.lang.String
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;A function called &lt;code&gt;getBodyPart&lt;/code&gt; couldn’t use the input it was receiving. &lt;code&gt;getBodyPart&lt;/code&gt; is used in only one part of Stop Slacking – to parse incoming emails (subscription requests, replies to notifications, emails to me).&lt;/p&gt;

&lt;p&gt;I had ignored the error messages. I wasn’t familiar with the function &lt;code&gt;getBodyPart&lt;/code&gt; because I hadn’t written this bit of code. I told myself that if it was really a problem, I’d hear about it or see the problem myself (hah).&lt;/p&gt;

&lt;p&gt;Why are only some emails causing an error? What’s different about these emails?&lt;/p&gt;

&lt;h2 id="step-1-recreate-the-error"&gt;Step 1: Recreate the error&lt;/h2&gt;

&lt;p&gt;In a Clojure REPL, I put an unsuccessful email through my email parsing function and received the error I expected:&lt;/p&gt;

&lt;div class="language-text highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;java.lang.IllegalArgumentException: No matching method found: getBodyPart for class java.lang.String
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;I have an error in an environment I can play with. Check.&lt;/p&gt;

&lt;p&gt;Next, I can step through the function to see what my computer sees. (Fingers crossed the computers remember my empathy when they take over.)&lt;/p&gt;

&lt;h2 id="step-2-diagnosing-the-problem"&gt;Step 2: Diagnosing the problem&lt;/h2&gt;

&lt;p&gt;Here’s the Clojure function my computer is trying to carry out when the error occurs:&lt;/p&gt;

&lt;div class="language-clojure highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;defn&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;parse-email&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;  [email]
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;  (&lt;span style="color:#080;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt; (MimeMessage.
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;      (Session/getDefaultInstance (java.util.Properties.))
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;      (io/input-stream (&lt;span style="color:#080;font-weight:bold"&gt;.&lt;/span&gt;getBytes (&lt;span style="color:#A60"&gt;:content&lt;/span&gt; email) &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;UTF-8&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;)))
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;      (&lt;span style="color:#080;font-weight:bold"&gt;.&lt;/span&gt;getContent)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n7" name="n7"&gt;7&lt;/a&gt;&lt;/span&gt;      (&lt;span style="color:#080;font-weight:bold"&gt;.&lt;/span&gt;getBodyPart &lt;span style="color:#00D"&gt;0&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n8" name="n8"&gt;8&lt;/a&gt;&lt;/span&gt;      (&lt;span style="color:#080;font-weight:bold"&gt;.&lt;/span&gt;getContent)))
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;You can see the &lt;code&gt;.getBodyPart&lt;/code&gt; function in the middle.&lt;/p&gt;

&lt;p&gt;Why does &lt;code&gt;.getBodyPart&lt;/code&gt; throw an error when it gets a string? What are the successful emails giving &lt;code&gt;.getBodyPart&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;To see what is happening at each step, I’ve removed the last two instructions from this function and I’ll add them back one at a time.&lt;/p&gt;

&lt;div class="language-clojure highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;defn&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;parse-email&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;  [email]
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;  (&lt;span style="color:#080;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt; (MimeMessage.
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;      (Session/getDefaultInstance (java.util.Properties.))
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;      (io/input-stream (&lt;span style="color:#080;font-weight:bold"&gt;.&lt;/span&gt;getBytes (&lt;span style="color:#A60"&gt;:content&lt;/span&gt; email) &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;UTF-8&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;)))
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;      (&lt;span style="color:#080;font-weight:bold"&gt;.&lt;/span&gt;getContent)))
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Compare the outputs:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: left"&gt;Input&lt;/th&gt;
      &lt;th style="text-align: left"&gt;Output&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: left"&gt;Unsuccessful email&lt;/td&gt;
      &lt;td style="text-align: left"&gt;&lt;code&gt;"Hi, this is the body of the email. Such email text."&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: left"&gt;Successful email&lt;/td&gt;
      &lt;td style="text-align: left"&gt;&lt;code&gt;#object[javax.mail.internet.MimeMultipart 0x6af1733c "javax.mail.internet.MimeMultipart@6af1733c"]&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;When I input the unsuccessful email into &lt;code&gt;parse-email&lt;/code&gt;, I get the message body.
When I input the successful email, I get an object identifier back.&lt;/p&gt;

&lt;p&gt;What happens when I follow the next instruction in my program, &lt;code&gt;.getBodyPart&lt;/code&gt;?&lt;/p&gt;

&lt;div class="language-clojure highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;defn&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;parse-email&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;  [email]
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;  (&lt;span style="color:#080;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt; (MimeMessage.
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;      (Session/getDefaultInstance (java.util.Properties.))
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;      (io/input-stream (&lt;span style="color:#080;font-weight:bold"&gt;.&lt;/span&gt;getBytes (&lt;span style="color:#A60"&gt;:content&lt;/span&gt; msg) &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;UTF-8&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;)))
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;      (&lt;span style="color:#080;font-weight:bold"&gt;.&lt;/span&gt;getContent)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n7" name="n7"&gt;7&lt;/a&gt;&lt;/span&gt;      (&lt;span style="color:#080;font-weight:bold"&gt;.&lt;/span&gt;getBodyPart &lt;span style="color:#00D"&gt;0&lt;/span&gt;)))
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The outputs:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: left"&gt;Input&lt;/th&gt;
      &lt;th style="text-align: left"&gt;Output&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: left"&gt;Unsuccessful email&lt;/td&gt;
      &lt;td style="text-align: left"&gt;&lt;code&gt;java.lang.IllegalArgumentException: No matching method found: getBodyPart for class java.lang.String&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: left"&gt;Successful email&lt;/td&gt;
      &lt;td style="text-align: left"&gt;&lt;code&gt;#object[javax.mail.internet.MimeBodyPart 0x39370e77 "javax.mail.internet.MimeBodyPart@39370e77"]&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The unsuccessful email gets me the error that started this quest. The successful email gets another object. It’s a different identifier this time, interesting.&lt;/p&gt;

&lt;p&gt;If I had stopped manipulating the unsuccessful email in the last step, I would’ve had what I wanted.&lt;/p&gt;

&lt;p&gt;I want to see how my successful email message makes it through this program. What happens when I add the second &lt;code&gt;.getContent&lt;/code&gt; from my original function?&lt;/p&gt;

&lt;div class="language-clojure highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;defn&lt;/span&gt; &lt;span style="color:#06B;font-weight:bold"&gt;parse-email&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;  [email]
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;  (&lt;span style="color:#080;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt; (MimeMessage.
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;      (Session/getDefaultInstance (java.util.Properties.))
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;      (io/input-stream (&lt;span style="color:#080;font-weight:bold"&gt;.&lt;/span&gt;getBytes (&lt;span style="color:#A60"&gt;:content&lt;/span&gt; msg) &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;UTF-8&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;)))
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;      (&lt;span style="color:#080;font-weight:bold"&gt;.&lt;/span&gt;getContent)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n7" name="n7"&gt;7&lt;/a&gt;&lt;/span&gt;      (&lt;span style="color:#080;font-weight:bold"&gt;.&lt;/span&gt;getBodyPart &lt;span style="color:#00D"&gt;0&lt;/span&gt;)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n8" name="n8"&gt;8&lt;/a&gt;&lt;/span&gt;      (&lt;span style="color:#080;font-weight:bold"&gt;.&lt;/span&gt;getContent)))
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The outputs:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style="text-align: left"&gt;Input&lt;/th&gt;
      &lt;th style="text-align: left"&gt;Output&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style="text-align: left"&gt;Unsuccessful email&lt;/td&gt;
      &lt;td style="text-align: left"&gt;(Not applicable)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style="text-align: left"&gt;Successful email&lt;/td&gt;
      &lt;td style="text-align: left"&gt;&lt;code&gt;"Hi, this is the body of the email. Such email text."&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;In summary, the full set of instructions works for some emails but is too much for other emails.&lt;/p&gt;

&lt;p&gt;What’s the difference between these two types of emails?&lt;/p&gt;

&lt;p&gt;Take a look at the emails themselves. (Note: These have been simplified for readability.)&lt;/p&gt;

&lt;p&gt;The successful email:&lt;/p&gt;

&lt;div class="language-text highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;Return-Path: &amp;lt;sender@example.com&amp;gt;
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;Subject: test email
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;To: recipient@example.com
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;Content-Type: multipart/alternative; boundary=\"123\"
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;--123
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;Content-Type: text/plain; charset=\"UTF-8\"
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n7" name="n7"&gt;7&lt;/a&gt;&lt;/span&gt;Hi, this is the body of the email. Such email text.
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n8" name="n8"&gt;8&lt;/a&gt;&lt;/span&gt;--123
&lt;span class="line-numbers"&gt; &lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n9" name="n9"&gt;9&lt;/a&gt;&lt;/span&gt;Content-Type: text/html; charset=\"UTF-8\"
&lt;span class="line-numbers"&gt;&lt;strong&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;&lt;/span&gt;Content-Transfer-Encoding: quoted-printable
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n11" name="n11"&gt;11&lt;/a&gt;&lt;/span&gt;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;style&amp;gt;body{font-family:Helvetica,Arial;font-size:13px}&amp;lt;/style&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;div&amp;gt;Hi, this is the body of the email. Such email text.&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n12" name="n12"&gt;12&lt;/a&gt;&lt;/span&gt;--123--
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;The unsuccessful email:&lt;/p&gt;

&lt;div class="language-text highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;Return-Path: &amp;lt;sender@example.com&amp;gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;Subject: test email
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;To: recipient@example.com
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;Content-Type: text/plain; charset=\"UTF-8\"
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20180716_stopslacking-multipart/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;Hi, this is the body of the email. Such email text.
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;You can see that the successful email starts with a &lt;code&gt;Content-Type: multipart&lt;/code&gt; and contains two children, &lt;code&gt;text/plain&lt;/code&gt; and &lt;code&gt;text/html&lt;/code&gt;. The unsuccessful email has only &lt;code&gt;Content-Type: text/plain&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is the crux of the problem. My program expected only multipart emails, not plain text, or what I like to call “singlepart”, emails.&lt;/p&gt;

&lt;p&gt;Diagnosis complete.&lt;/p&gt;

&lt;h2 id="step-3-resolve-the-problem"&gt;Step 3: Resolve the problem.&lt;/h2&gt;

&lt;p&gt;Now that I knew the problem, I knew that I needed to detect whether an email was single or multipart and then parse accordingly.&lt;/p&gt;

&lt;p&gt;Because I don’t know which order the body parts will arrive in, my new function lists the body parts and their types so that I can select the longest of each type. For example, if there are two plain text parts in the email, I grab the longer one and parse it. If needed, I can select html and fall back on the plain text.&lt;/p&gt;

&lt;p&gt;I’ve a newfound admiration for the folks that build email clients and email services because they must anticipate a forest full of sneaky edge cases just waiting to pounce.&lt;/p&gt;

&lt;h2 id="playing-with-email-clients"&gt;Playing with email clients&lt;/h2&gt;

&lt;p&gt;I’ve discovered that Fastmail sends plain text emails by default and sends multipart emails in response to multipart emails.&lt;/p&gt;

&lt;p&gt;You can write and send plain text emails from Gmail if you select “Plain text mode” from the little dropdown in the bottom right corner, to the right of the trash icon. This is handy for testing.&lt;/p&gt;

&lt;p&gt;You can also examine the raw email in Gmail by selecting the “Show original” option from the top right dropdown, to the right of the reply arrow on an individual email message.&lt;/p&gt;

&lt;p&gt;Now you can look at the email messages you’ve received and see if they’re multipart messages or single part plain text messages.&lt;/p&gt;

&lt;p&gt;It’s cool that email supports a variety of options because we’re all different people with different needs.&lt;/p&gt;

&lt;h2 id="product-design-footnote"&gt;Product Design Footnote&lt;/h2&gt;

&lt;p&gt;Is it worthwhile to support plain text emails? Are you going to support every little edge case?&lt;/p&gt;

&lt;p&gt;Plain text emails aren’t just an edge case for Stop Slacking.&lt;/p&gt;

&lt;p&gt;Folks who want to use lower bandwidth tools like Stop Slacking are highly likely to use plain text email because it is the lowest bandwidth type of email.&lt;/p&gt;

&lt;p&gt;The primary purpose of Stop Slacking is to offer a lower bandwidth method for using Slack. I want to support my users in their efforts to use other low bandwidth tools.&lt;/p&gt;

&lt;p&gt;This is probably the easiest product design decision I’ve ever made. :)&lt;/p&gt;

&lt;p&gt;Drop me a line if you have any questions, have encountered odd types of emails in your work, or know of a set of raw emails that could be used for testing email parsers.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20180609_stopslacking/</id>
    <title>Stop Slacking: integrating Slack into email</title>
    <published>2018-06-17T00:00:00Z</published>
    <updated>2018-06-17T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20180609_stopslacking/" type="text/html"/>
    <content type="html">&lt;h1 id="stop-slacking-br-integrating-slack-into-email"&gt;Stop Slacking: &lt;br&gt; integrating Slack into email&lt;/h1&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20180609_stopslacking/slack.png" alt="" style="border: 0"&gt;
  &lt;figcaption&gt;Slack is an endless meeting with no agenda.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;div class="info"&gt;
  &lt;div class="info-header"&gt;
    &lt;p class="info-title"&gt;Update&lt;/p&gt;
    &lt;p class="info-date"&gt;December 21, 2025&lt;/p&gt;
  &lt;/div&gt;
  &lt;p&gt;This project has been retired. After a couple years, Slack finally introduced this feature. :)&lt;/p&gt;
&lt;/div&gt;

&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;To minimize my data usage and increase my productivity, my partner, &lt;a href="https://kevinlynagh.com/"&gt;Kevin,&lt;/a&gt; and I built a &lt;strong&gt;reply-via-email service&lt;/strong&gt; for &lt;a href="https://slack.com/"&gt;Slack&lt;/a&gt; (a chat app used by many businesses for internal communication).&lt;/p&gt;

&lt;p&gt;Stop Slacking sends you an email when you are mentioned or direct messaged on Slack (the important stuff) and then posts your reply to Slack.&lt;/p&gt;

&lt;p&gt;Try it out at &lt;a href="https://www.stopslacking.com/"&gt;stopslacking.com.&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="the-setting-or-how-to-work-in-a-campervan"&gt;The Setting (or How to Work in a Campervan)&lt;/h2&gt;

&lt;p&gt;During April, Kevin and I lived out of a converted minivan in southern New Zealand. On a given morning I’d pat myself on the back for my life choices and then spend the next hour agonizing over connectivity.&lt;/p&gt;

&lt;p&gt;When wi-fi wasn’t working well, I moved into range of a cell tower and watched the MBs rack up at $10/GB. I quickly became aware of how much data I was using.&lt;/p&gt;

&lt;p&gt;In short, I didn’t want to be opening Slack more than I needed to. Email was faster and cost less, not to mention it helps me stay focused.&lt;/p&gt;

&lt;h2 id="the-big-product-design-decisions"&gt;The Big Product Design Decisions&lt;/h2&gt;

&lt;p&gt;The design of Stop Slacking started with a few key decisions about medium and functionality. I’ve outlined these decisions in order of how much different Stop Slacking would have been if we’d made different choices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Stop Slacking is not an official Slack App.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The scope needs to stay small. We just wanted to get something working. The alternative to making an official Slack App is to use their legacy API tokens. A user can access a token for their account on a given workspace and send it to Stop Slacking to create a subscription. Administrators can disallow this functionality and thus control access to their data.&lt;/p&gt;

&lt;p&gt;This decision chooses speed over making sure the service works for every workspace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The interface will be email.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Stop Slacking lives only in email. All commands and feedback are sent via email. There’s no web interface; it’s a text interface like a terminal program.&lt;/p&gt;

&lt;p&gt;This satisfies our goal of minimizing data use. Email is a data-light communication channel. We don’t have to worry about keeping a web interface light-weight.&lt;/p&gt;

&lt;p&gt;Choosing to use a text interface means we need to design for discoverability. All emails from Stop Slacking include footers enumerating options for interacting with Stop Slacking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Replies are posted in Slack as threaded replies.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Slack has two ways to reply to a message. The original method was a general reply, meaning that the reply appeared at the bottom of the channel. Later, Slack added threaded replies. A threaded reply defaults to being truncated beneath the parent message and the entire thread can be opened in a side panel. This is useful when discussions on multiple topics carry on simultaneously in the same channel.&lt;/p&gt;

&lt;p&gt;When you reply to a Slack message, do you want it to be a general reply or a threaded reply?&lt;/p&gt;

&lt;p&gt;Specifying this choice with each message requires you to type a keyword or character to indicate a general or threaded reply. The error potential goes through the roof for this functionality. Since we’re using text, we’d have to correctly identify your command and separate it from your reply and the quoted email that most email clients include. Could we predict all the ways that could go wrong and prevent those errors?&lt;/p&gt;

&lt;p&gt;A preference setting might be a solution for people who have a strong preference one way or another but not for people who use both general and threaded replies regularly.&lt;/p&gt;

&lt;p&gt;I made the call to keep the product simple with threaded replies only. The reply will always be in-context, even if you are replying hours later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. @ mentions and direct messages&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Which Slack messages do you want to receive notifications about?&lt;/p&gt;

&lt;p&gt;For a busy person, these @ mentions and direct messages cover their most urgent tasks.&lt;/p&gt;

&lt;p&gt;On company I work with has such an overflow of Slack messages that the team has learned to @ mention every time they address someone. Urgent has become the norm. (Red flag, anyone?)&lt;/p&gt;

&lt;p&gt;Notification source preferences are on the idea list for future features.&lt;/p&gt;

&lt;h2 id="omitted-for-brevity"&gt;Omitted for Brevity&lt;/h2&gt;

&lt;p&gt;In the future, I’ll write more about designing the emails (when and what, variation between quoting and threading in email clients) and implementation (a Clojure app on a server, the underside of Slack APIs, and a handful of AWS consoles - S3, SES, SNS, ACM, Cloudfront).&lt;/p&gt;

&lt;p&gt;What do you want to hear about?&lt;/p&gt;

&lt;h2 id="whats-next"&gt;What’s Next?&lt;/h2&gt;

&lt;p&gt;I’ve enjoyed working on a text-only interface. And the landing page was fun to build - especially those high-quality “screenshots”.&lt;/p&gt;

&lt;p&gt;My task now is to test our design decisions against the real-life needs of other people. How do other people want to use Stop Slacking? What do they need to improve their workflow?&lt;/p&gt;

&lt;p&gt;I’d love to hear your comments and questions. Feel free to email me at &lt;a href="mailto:nicki@stopslacking.com"&gt;nicki@stopslacking.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Update July 2, 2018: Kevin has posted &lt;a href="https://kevinlynagh.com/stop-slacking/"&gt;a stellar article&lt;/a&gt; that covers another side to the product design and discusses what’s under the hood – the development considerations unique to email, Slack, and low bandwidth.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20180609_stopslacking/email-msg.png" alt="" style="border: 0"&gt;
  &lt;figcaption&gt;My "screenshot" of an email reply.&lt;/figcaption&gt;
&lt;/figure&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20171221_boatsie/</id>
    <title>Floatin' my boat with CSS animation: Troubleshooting stutter caused by animating layout properties</title>
    <published>2017-12-21T00:00:00Z</published>
    <updated>2017-12-21T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20171221_boatsie/" type="text/html"/>
    <content type="html">&lt;h1 id="floatin-my-boat-with-css-animation-br-troubleshooting-stutter-caused-by-animating-layout-properties"&gt;Floatin’ my boat with CSS animation: &lt;br&gt; Troubleshooting stutter caused by animating layout properties&lt;/h1&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;video controls="true" src="https://nickivance.com/writing/20171221_boatsie/boatsie7.mp4" autoplay="true" height="300px" loop="true" poster="poster.png"&gt;&lt;/video&gt;
  &lt;figcaption&gt;Meep meep!&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I’ve been building a simple CSS animation of a boat traveling across the viewport, puffing smoke as it goes. This article is a write-up for others who are building CSS animations and might benefit from a few of the things I learned on this project, namely:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;why you shouldn’t animate properties that affect layout&lt;/li&gt;
  &lt;li&gt;how to layer multiple animations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are new discoveries. I just want to add to the examples out there that we all use to help each other learn.&lt;/p&gt;

&lt;p&gt;If you were hoping for a good beginner’s tutorial, I highly recommend &lt;a href="https://www.kirupa.com/html5/learn_animation.htm"&gt;Kirupa’s free tutorials.&lt;/a&gt; These are clear, to-the-point, and come in both article and video formats.&lt;/p&gt;

&lt;h2 id="the-basic-boat"&gt;The Basic Boat&lt;/h2&gt;

&lt;p&gt;First, I drew a little boat in Sketch and exported it as an svg. From the Sketch svg, I copied out the lines that were clearly useful and discarded the additional info.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20171221_boatsie/boatsie.svg" alt="" style="border: 0"&gt;
  &lt;figcaption&gt;This little tugboat is called "Boatsie".&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The animation comes in three parts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;the boat moving side-to-side&lt;/li&gt;
  &lt;li&gt;the boat rocking up and down&lt;/li&gt;
  &lt;li&gt;the engine smoke puffing up and away behind the boat&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="transitions-vs-animations"&gt;Transitions vs Animations&lt;/h2&gt;

&lt;p&gt;It’s always worth asking yourself if you should be using a transition or an animation. In my case, I want this animation to run continuously so I need to use the animation property. For more discussion about animation vs transition, see the &lt;a href="https://cssanimation.rocks/transition-vs-animation/"&gt;cssanimation.rocks article on the topic.&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="moving-side-to-side"&gt;Moving Side-to-side&lt;/h2&gt;

&lt;p&gt;First, I made the boat move side-to-side. This animation is created by changing the margin-left property. I borrowed this idea from somewhere else but the source escapes me. We’ll talk later about why animating margin-left isn’t a great idea.&lt;/p&gt;

&lt;p&gt;The animation loop consists of moving across the screen, flipping the boat when it reaches the end of the page using the &lt;code&gt;transform: rotateY()&lt;/code&gt; property, and moving back across the screen.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;video controls="true" src="https://nickivance.com/writing/20171221_boatsie/boatsie1.mp4" autoplay="true" height="300px" loop="true" poster="poster.png"&gt;&lt;/video&gt;
&lt;/figure&gt;

&lt;h2 id="rocking-up-and-down"&gt;Rocking up and down&lt;/h2&gt;

&lt;p&gt;The rocking motion is an animation of the &lt;code&gt;transform: rotate&lt;/code&gt; property. This is the 2D rotate property.&lt;/p&gt;

&lt;p&gt;I didn’t want the edges of the bottom of the boat to come out of the water because it would ruin the illusion that this is a boat sitting in water. For this effect, I positioned the boat lower and used the boat’s parent div as a clipping path using an inset rectangle (&lt;a href="https://css-tricks.com/clipping-masking-css/"&gt;CSS-Tricks article on clipping paths&lt;/a&gt;).&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;video controls="true" src="https://nickivance.com/writing/20171221_boatsie/boatsie2.mp4" autoplay="true" height="300px" loop="true" poster="poster.png"&gt;&lt;/video&gt;
&lt;/figure&gt;

&lt;p&gt;With a clipping path:&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;video controls="true" src="https://nickivance.com/writing/20171221_boatsie/boatsie3.mp4" autoplay="true" height="300px" loop="true" poster="poster.png"&gt;&lt;/video&gt;
&lt;/figure&gt;

&lt;p&gt;I ended up adding the rocking motion to the same animation that moves the boat side-to-side. I would like a way to combine multiple animations but I couldn’t because both animations used the same property, &lt;code&gt;transform&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the side-to-side animation, I have &lt;code&gt;transform: rotateY()&lt;/code&gt; and in the rocking, I have &lt;code&gt;transform: rotate()&lt;/code&gt;. Even though &lt;code&gt;rotate&lt;/code&gt; and &lt;code&gt;rotateY&lt;/code&gt; are different, they still define the transform. If I created two separate animations and applied them to the same element, the CSS would use the transform instructions from the latter animation. The highest rule of CSS: there can be only one [of each property declaration]!&lt;/p&gt;

&lt;h2 id="engine-smoke"&gt;Engine smoke&lt;/h2&gt;

&lt;p&gt;The smoke was a bit trickier than the other two movements. To begin with, the smoke needs to move with the boat. That’s easy, just apply the same side-to-side animation as the boat. Since the boat animation merges side-to-side and rocking motions, I had to create a new animation just for the side-to-side and apply this to the smoke.&lt;/p&gt;

&lt;p&gt;The smoke is made up of a container svg, which takes the side-to-side animation, and small svg circles as children.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;svg class="smoke" width="100px" height="40px" viewBox"0 0 153 40"&amp;gt;
   &amp;lt;circle class="one" fill="gray" cx="47" cy="35" r="4"&amp;gt;&amp;lt;/circle&amp;gt;
   &amp;lt;circle class="two" fill="gray" cx="47" cy="35" r="4"&amp;gt;&amp;lt;/circle&amp;gt;
&amp;lt;/svg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here’s the boat with smoke:&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;video controls="true" src="https://nickivance.com/writing/20171221_boatsie/boatsie4.mp4" autoplay="true" height="300px" loop="true" poster="poster.png"&gt;&lt;/video&gt;
&lt;/figure&gt;

&lt;p&gt;Next, I needed to make the smoke puff. I experimented with the movement and opacity in an animation on the first circle.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;video controls="true" src="https://nickivance.com/writing/20171221_boatsie/boatsie5.mp4" autoplay="true" height="300px" loop="true" poster="poster.png"&gt;&lt;/video&gt;
&lt;/figure&gt;

&lt;p&gt;This just took a bit of troubleshooting. Let me know if you have a tool for generating the frames of an animation. That would make this process a little easier than watching the animation over and over.&lt;/p&gt;

&lt;p&gt;I originally had three circles but when I started applying the same puffing animation to each circle and staggering them with a delay, I realized I might not need all three. Two seemed to do the trick.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;video controls="true" src="https://nickivance.com/writing/20171221_boatsie/boatsie6.mp4" autoplay="true" height="300px" loop="true" poster="poster.png"&gt;&lt;/video&gt;
&lt;/figure&gt;

&lt;p&gt;Looks pretty good, eh? You’ve probably noticed by now that when the boat flips and the smoke flips, a puff of smoke makes an incredible jump in space that indicates some very improbable fluid dynamics.&lt;/p&gt;

&lt;p&gt;I spent a bit of time drawing little timelines to visualize how I could make the puffing animation a factor of the boat’s animation so that the puffs would dissipate conveniently before the boat flips.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20171221_boatsie/timing.jpg" alt=""&gt;
  &lt;figcaption&gt;I mistakenly assumed that the delay occurs with every cycle of the animation.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;This syncing is something I still haven’t resolved. I’ve got bigger fish to fry.&lt;/p&gt;

&lt;h2 id="why-you-shouldnt-animate-layout-changes"&gt;Why you shouldn’t animate layout changes&lt;/h2&gt;

&lt;p&gt;My partner, &lt;a href="https://kevinlynagh.com/"&gt;Kevin&lt;/a&gt;, noticed that the animation wasn’t quite smooth.&lt;/p&gt;

&lt;p&gt;We recorded the animation using Chrome Dev Tools and found that nothing was slowing the animation down.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20171221_boatsie/perf.png" alt=""&gt;
  &lt;figcaption&gt;You can see that there is a frame rendered approximately every 16 seconds, as expected. The blocks of color show when the browser is thinking and there are gaps in there so we aren't pressed for time.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;How did we figure this out?&lt;/p&gt;

&lt;p&gt;The browser carries out any instructions one at a time. We’re computing on a single thread here. To animate a change, the browser has to calculate all those fancy in-between frames for us and render them.&lt;/p&gt;

&lt;p&gt;If carrying out computing instructions takes too long, the render doesn’t happen regularly and results in a visible lag. If the lag is great enough, the browser is smart enough that it can skip a frame to try and catch up. You might see an even larger visible jump in your animation.&lt;/p&gt;

&lt;p&gt;If you zoom in, Chrome Dev Tools shows you how long each of these instructions takes. It’s really incredible how much detail is in the performance readout. You can zoom in and out of huge amounts of data quickly and easily. This tool is really one of the best interactive data visualizations I’ve ever seen.&lt;/p&gt;

&lt;p&gt;In my case, you could see that the frames were still being rendered at the expected frame rate.&lt;/p&gt;

&lt;p&gt;At this point, my partner asked me how I was animating the boat. When I mentioned the margin-left, he explained two valuable lessons he has learned:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The browser rounds pixels, sometimes resulting in imprecise graphics.&lt;/li&gt;
  &lt;li&gt;Every time you change a layout property, the browser recalculates the entire layout, even if nothing but the element in question was affected.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;He suggested a quick test, push the browser width to extremes to see if the problem gets worse.&lt;/p&gt;

&lt;p&gt;Making the browser width very small had a telling result:&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;video controls="true" src="https://nickivance.com/writing/20171221_boatsie/stutter.mp4" autoplay="true" height="300px" loop="true" poster="poster.png"&gt;&lt;/video&gt;
&lt;/figure&gt;

&lt;p&gt;The duration of this animation is 10 seconds so the boat has to move the width of the viewport in 10 seconds. The browser has to divide up the width of the viewport into 10 seconds x 60 frames per second = 600 frames. When the browser divides the width of the viewport by 600, there is bound to be some rounding because it’s unlikely that the viewport width is a multiple of 600.&lt;/p&gt;

&lt;p&gt;Ok, so we have some pixel rounding. If my viewport is 601 pixels wide, my little boat will have to jump one extra pixel at some point.&lt;/p&gt;

&lt;p&gt;If my viewport is less than 600 pixels wide, then my boat will have to pause a lot because multiple frames will round to the same pixel position.&lt;/p&gt;

&lt;p&gt;I replaced all of my &lt;code&gt;margin-left&lt;/code&gt;s with &lt;code&gt;transform: translate&lt;/code&gt; and the result is much smoother.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;video controls="true" src="https://nickivance.com/writing/20171221_boatsie/boatsie7.mp4" autoplay="true" height="300px" loop="true" poster="poster.png"&gt;&lt;/video&gt;
&lt;/figure&gt;

&lt;h2 id="wrapping-up"&gt;Wrapping Up&lt;/h2&gt;

&lt;p&gt;This is where I’m stopping. I tried to make two more changes but got stuck.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;I still haven’t fixed the smoke puffs that flip when the boat reaches the end of the screen. I admit I’m stumped.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I want the boat to cross the screen at the same speed, regardless of viewport width.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On this second point, I tried using &lt;code&gt;calc&lt;/code&gt; to try and turn a &lt;code&gt;vw&lt;/code&gt; length value (px) into a time value (seconds). I couldn’t find a way to make it work.&lt;/p&gt;

&lt;p&gt;The other option is to use Javascript, something I’ve been avoiding with this whole project. It appeals to me to be able to use CSS as much as possible.&lt;/p&gt;

&lt;p&gt;Returning to my goals, when I put this on my website, I will probably have the boat go off the edges of the page instead of turning around so that will solve problem #1.&lt;/p&gt;

&lt;h2 id="props-to-glitchme"&gt;Props to Glitch.me&lt;/h2&gt;

&lt;p&gt;I began this animation using a cool live development tool called &lt;a href="http://glitch.me/"&gt;Glitch.me&lt;/a&gt;. It was a great way to get started quickly because Glitch creates the basic files for you to jump in and start editing. I didn’t have to worry about setting up my project files and creating a git repo. Also, Glitch has a cute interface that is wonderfully different than everything else out there.&lt;/p&gt;

&lt;p&gt;I eventually moved the code into a local git repo and finished building it there. I wanted to document the process of creating this animation. My favorite way to do this is by making commits in a git repo so I can roll back the code and snapshot significant steps.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;I’ll update here when I’ve added the animation to my site.
Thanks for reading!&lt;/p&gt;

&lt;h2 id="update"&gt;Update&lt;/h2&gt;

&lt;p&gt;I’ve added the animation to my site. To create a roughly similar boat speed on different size screens, I’ve used media queries.&lt;/p&gt;

&lt;p&gt;I discovered that Chrome made an update recently that undoes my clip-path on the boat. I was totally stumped so Kevin was super helpful again. He is quick about coming up with hypotheses for what might be wrong AND simple tests to check if the hypothesis is on the right track. He turned off the boat’s animation and found that the clip-path worked again. So animation is part of the problem. He also tried tinkering with the z-index of the boat and that showed us that by changing the z-index, we could undo the clipping on the still boat. Kevin said there might be a bug in Chrome that is causing the animation to composite funny. Perhaps someone who enjoys digging into bugs like this can provide an explanation. :)&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20171212_concept-design-talk/</id>
    <title>Concept Design: A lightning talk for the Melbourne Web Meetup</title>
    <published>2017-12-12T00:00:00Z</published>
    <updated>2017-12-12T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20171212_concept-design-talk/" type="text/html"/>
    <content type="html">&lt;h1 id="concept-design-a-lightning-talk-for-the-melbourne-web-meetup"&gt;Concept Design: A lightning talk for the Melbourne Web Meetup&lt;/h1&gt;

&lt;p&gt;Title: Why writing about concepts before you start sketching will win you money, respect, or well, at least warm fuzzy feelings&lt;/p&gt;

&lt;p&gt;A product is, at its heart, a collection of ideas linked together in a greater whole. Like a building’s foundation we want the core ideas to be sound and mutually supportive. Design processes don’t address these core ideas adequately, often resulting in awkward experiences.&lt;/p&gt;

&lt;p&gt;My talk is a brief introduction to the practice of concept design. The talk was originally titled “Why writing about concepts before you start sketching will win you money, respect, or well, at least warm fuzzy feelings”. When done well and included at the beginning of a project, concept design has the potential to avoid major changes in design, thus saving money and frustration. It’s up to you to convert that into direct benefits to yourself.&lt;/p&gt;

&lt;p&gt;For more information, check out the work &lt;a href="http://people.csail.mit.edu/dnj/"&gt;Daniel Jackson at MIT&lt;/a&gt; has done to define and establish concept design with his students, in particular Santiago Perez De Rosso.&lt;/p&gt;

&lt;p&gt;Paper: &lt;a href="https://groups.csail.mit.edu/sdg/pubs/2015/concept-essay.pdf"&gt;Towards a Theory of Conceptual Design for Software&lt;/a&gt;
Paper:&lt;a href="https://spderosso.github.io/onward13.pdf"&gt;What’s Wrong with Git? A Conceptual Design Analysis&lt;/a&gt;
Talk: &lt;a href="https://www.youtube.com/watch?v=LDny7VGLzTE"&gt;SATURN 2016 Keynote: Rethinking Software Design&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can take a look at the &lt;a href="http://www.nickivance.com/writing/20171212_concept-design-talk/concept-design.pdf"&gt;slides&lt;/a&gt; or watch the video:&lt;/p&gt;

&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/bwv3jZ7rG-A" frameborder="0" gesture="media" allow="encrypted-media" allowfullscreen=""&gt;&lt;/iframe&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20171129_compiling-clojurescript/</id>
    <title>Compiling Clojurescript</title>
    <published>2017-11-29T00:00:00Z</published>
    <updated>2017-11-29T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20171129_compiling-clojurescript/" type="text/html"/>
    <content type="html">&lt;h1 id="compiling-clojurescript"&gt;Compiling Clojurescript&lt;/h1&gt;

&lt;p&gt;I am making little web games in Clojurescript and I want to be able to put them up on github so my friends can play them. So far I have been working in a live-reloading-dev environment so I haven’t given this problem any thought.&lt;/p&gt;

&lt;p&gt;After a bit of fuss, trying to read lots of tutorials and looking at code my partner wrote for one of our projects, I figured it out. I’m writing it up here for two reasons.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;So I can retrace my steps next time I need to set up compilation code in a Clojurescript project (it can vary each time!).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;For anyone else who is looking for help.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I looked at these tutorials but neither gave me a working answer out-of-the-box.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://clojurescript.org/guides/quick-start"&gt;https://clojurescript.org/guides/quick-start&lt;/a&gt; (I ran into a namespace error while following this tutorial. I think it was resolved by the bash code below that grabs the classpath from leiningen. Writing the correct classpaths when you have lots of pieces in multiple directories is hard!)&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.flyingmachinestudios.com/programming/how-clojure-babies-are-made-lein-run/"&gt;http://www.flyingmachinestudios.com/programming/how-clojure-babies-are-made-lein-run/&lt;/a&gt; (This has tons of great detail but is a lot of new concepts for me to handle.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don’t get me wrong - the guys who wrote these have created tons of awesome resources that are very accessible!&lt;/p&gt;

&lt;h2 id="heres-what-worked-for-me"&gt;Here’s what worked for me!&lt;/h2&gt;

&lt;p&gt;Make sure I have the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;source files (this is usually a directory and can contain multiple files that you want to be compiled)&lt;/li&gt;
  &lt;li&gt;cljs.jar&lt;/li&gt;
  &lt;li&gt;build.clj&lt;/li&gt;
  &lt;li&gt;start.sh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The cljs.jar is a big library. I’m not 100% sure it’s a library because I just needed to make sure it was in the right place after I downloaded it. I didn’t need to edit it or open it.&lt;/p&gt;

&lt;p&gt;The build.clj file is the clojure code that specifies what you want compiled. Mine contained:&lt;/p&gt;

&lt;div class="language-clojure highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20171129_compiling-clojurescript/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;(&lt;span style="color:#080;font-weight:bold"&gt;require&lt;/span&gt; 'cljs.build.api)
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20171129_compiling-clojurescript/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;(cljs.build.api/build &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;src&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt; {&lt;span style="color:#A60"&gt;:output-to&lt;/span&gt; &lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;resources/public/js/outs/main.js&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;})
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;code&gt;src&lt;/code&gt; is the directory path that contains everything I want compiled.&lt;/p&gt;

&lt;p&gt;The string after &lt;code&gt;:output-to&lt;/code&gt; is the directory path and file name that I want created. (It needs to be a javascript file because that’s what we’re outputting! But I haven’t tried anything else so I’m not sure what mischief might occur.)&lt;/p&gt;

&lt;p&gt;The directory paths are relative to the location of the build.clj file. (BTW, you can name the build file other things, but “build” or “builds” seems common.)&lt;/p&gt;

&lt;p&gt;The start.sh file makes using the terminal easier. It contains:&lt;/p&gt;

&lt;div class="language-bash highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20171129_compiling-clojurescript/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;#!/bin/bash
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20171129_compiling-clojurescript/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;JVM_OPTIONS="
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20171129_compiling-clojurescript/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;  -cp `lein classpath`
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20171129_compiling-clojurescript/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;  -Djava.awt.headless=true
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20171129_compiling-clojurescript/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;  -client
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20171129_compiling-clojurescript/#n6" name="n6"&gt;6&lt;/a&gt;&lt;/span&gt;  -XX:+UseConcMarkSweepGC
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20171129_compiling-clojurescript/#n7" name="n7"&gt;7&lt;/a&gt;&lt;/span&gt;  -XX:+CMSClassUnloadingEnabled"
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20171129_compiling-clojurescript/#n8" name="n8"&gt;8&lt;/a&gt;&lt;/span&gt;rlwrap java $JVM_OPTIONS clojure.main build.clj development
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;I think the first line says, “hey, we’re talking in bash” so the terminal or shell (what’s the difference?) knows which language we’re in. I owe &lt;a href="https://kevinlynagh.com/"&gt;Kevin Lynagh&lt;/a&gt; for writing this file and most of this app set up.&lt;/p&gt;

&lt;p&gt;I forget what rlwrap does. I use it for starting figwheel, too.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code&gt;java&lt;/code&gt; is the command or program we’re invoking.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The &lt;code&gt;$JVM_OPTIONS&lt;/code&gt; looks like a list of options for invoking &lt;code&gt;java&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code&gt;clojure.main&lt;/code&gt; is not something I understand.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code&gt;build.clj&lt;/code&gt; is the name of the build file you want to run.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code&gt;development&lt;/code&gt; is…?&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that I have these files in place, I navigate to my project directory in the Terminal and run &lt;code&gt;./start.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This gets everything going!&lt;/p&gt;

&lt;p&gt;You should see a folder labeled &lt;code&gt;out&lt;/code&gt; in your current directory. This contains all the code for the dependencies not in your source.
You should also be able to find the .js file you defined as the output of your build.clj file.&lt;/p&gt;

&lt;p&gt;You’ll need an index.html file that contains:&lt;/p&gt;

&lt;p&gt;~~~html&lt;/p&gt;

    
      &lt;meta charset="utf-8"&gt;
      &lt;link href="https://nickivance.com/writing/20171129_compiling-clojurescript/css/style.css" rel="stylesheet" type="text/css"&gt;
    
    
      &lt;div id="container"&gt;&lt;/div&gt;
      
      
      
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20171128_animating-tile-flip/</id>
    <title>Animating A Tile Flip For An Adventure Game With CSS</title>
    <published>2017-11-28T00:00:00Z</published>
    <updated>2017-11-28T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20171128_animating-tile-flip/" type="text/html"/>
    <content type="html">&lt;h1 id="animating-a-tile-flip-for-an-adventure-game-with-css"&gt;Animating A Tile Flip For An Adventure Game With CSS&lt;/h1&gt;

&lt;p&gt;I was reading a &lt;a href="http://ludumdare.com/compo/2014/05/04/the-sun-and-moon-post-mortem/"&gt;game post mortem&lt;/a&gt; from an awesome indie game developer, Daniel Linssen, about some ideas he’d had for The Sun and The Moon:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“My first idea was to make either a top down game or a platformer where you could use the mouse to flip tiles and expose what was beneath the surface. By flipping tiles you could create paths, remove spikes, block enemies, and so on.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The game he ended up building, The Sun and The Moon, was very different from this idea. There are no tiles that flip.&lt;/p&gt;

&lt;p&gt;I wanted to see what this dynamic would be like, the mouse creating a path ahead of a character that revealed what was underneath, on the flipside of the world. With a slight delay, there would be the opportunity to undo the path before the character arrived and seek out a better one. I’m not sure about spikes and enemies yet. But it felt like a great game mechanic to prototype. It’s also conceivably within reach but I’d still need to learn a lot.&lt;/p&gt;

&lt;p&gt;Here’s the initial prototype:&lt;/p&gt;

&lt;iframe src="https://player.vimeo.com/video/244026129" width="640" height="633" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;p&gt;This worked out pretty nicely.&lt;/p&gt;

&lt;h2 id="making-the-animation-less-choppy"&gt;Making the animation less choppy&lt;/h2&gt;

&lt;p&gt;I asked a few friends who have a good eye for this stuff for suggestions and they were wonderfully helpful!&lt;/p&gt;

&lt;p&gt;The first suggestion I tackled (the only one I discuss in this post) was that the animation is a bit choppy. It seems to skip forward.&lt;/p&gt;

&lt;p&gt;By the way, this is built using Clojurescript which means the animations were done in CSS. I used CSS animations, not CSS transitions. I used animations because I wanted to introduce a property and then make it go away over the course of the animation. E.g. Start at 0, go to 50, and then go back to 0. Transitions only introduce a property and make the change. That would just be starting at 0 and going to 50.&lt;/p&gt;

&lt;p&gt;I’ve come up with two possibilities for the choppiness:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve asked my keyframes to move 45% of the total distance travelled in 25% of the total time.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;I’ve been changing layout properties in my animations. This causes the browser to spend a bunch of time calculating how the layout changes affect everything else in the layout. Too much thinking = slow.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There might be other possibilities. My computer only has 4GB of RAM and every program thinks it should have all of it so my computer is constantly running close the edge of its abilities. (Edit: I found out later that I was getting worse choppiness when my computer is over-stressed so I doubt that when it was working better it was still because of the memory. Unless memory scarcity declines in stages instead of all at once??)&lt;/p&gt;

&lt;p&gt;As usual, instead of testing what’s causing the delay, I just jumped into solving one possibility. It did seem like good form to remove the layout properties from the animation anyway.&lt;/p&gt;

&lt;h2 id="recreating-the-tile-flip-without-layout-properties"&gt;Recreating the tile flip without layout properties&lt;/h2&gt;

&lt;p&gt;I found a nice tutorial about &lt;a href="http://desandro.github.io/3dtransforms/docs/card-flip.html"&gt;creating the tile flip animation&lt;/a&gt;, from David DeSandro. Here’s the resulting tile flip:&lt;/p&gt;

&lt;iframe src="https://player.vimeo.com/video/244596873" width="640" height="647" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;h2 id="a-cool-perspective"&gt;A cool perspective&lt;/h2&gt;

&lt;p&gt;While exploring the card flip tutorial, I found that I could make the whole game field look like you were looking at it from an angle above.&lt;/p&gt;

&lt;iframe src="https://player.vimeo.com/video/244596885" width="640" height="505" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;p&gt;I really like this but when I use it, it cancels out the perspective on the flipping tile. Notice how the tile flip looks more like an elevator door opening and closing. It’s not coming up off the plane of the flat tiles.&lt;/p&gt;

&lt;h2 id="getting-everything-i-want"&gt;Getting everything I want&lt;/h2&gt;

&lt;p&gt;I’m not quite sure why I can’t have everything I want. It might take a bit of investigation. I think it has something to do with only being able to use the transform property once for each element. Here’s the pseudo code:&lt;/p&gt;

&lt;p&gt;HTML Structure:&lt;/p&gt;

&lt;div class="language-html highlighter-coderay"&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20171128_animating-tile-flip/#n1" name="n1"&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#070;font-weight:bold"&gt;&amp;lt;div&lt;/span&gt; &lt;span style="color:#b48"&gt;class&lt;/span&gt;=&lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;box-with-perspective-view&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span style="color:#070;font-weight:bold"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#777"&gt;&amp;lt;!--apply perspective--&amp;gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20171128_animating-tile-flip/#n2" name="n2"&gt;2&lt;/a&gt;&lt;/span&gt;   &lt;span style="color:#070;font-weight:bold"&gt;&amp;lt;div&lt;/span&gt; &lt;span style="color:#b48"&gt;class&lt;/span&gt;=&lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;grid-that-contains-small-tiles&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span style="color:#070;font-weight:bold"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#777"&gt;&amp;lt;!--use transform: preserve 3d to keep perspective of parent--&amp;gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20171128_animating-tile-flip/#n3" name="n3"&gt;3&lt;/a&gt;&lt;/span&gt;      &lt;span style="color:#070;font-weight:bold"&gt;&amp;lt;div&lt;/span&gt; &lt;span style="color:#b48"&gt;class&lt;/span&gt;=&lt;span style="background-color:hsla(0,100%,50%,0.05)"&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;span style="color:#D20"&gt;tile-in-the-grid&lt;/span&gt;&lt;span style="color:#710"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span style="color:#070;font-weight:bold"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#070;font-weight:bold"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt; &lt;span style="color:#777"&gt;&amp;lt;!--use transform to rotate--&amp;gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20171128_animating-tile-flip/#n4" name="n4"&gt;4&lt;/a&gt;&lt;/span&gt;   &lt;span style="color:#070;font-weight:bold"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="line-numbers"&gt;&lt;a href="https://nickivance.com/writing/20171128_animating-tile-flip/#n5" name="n5"&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span style="color:#070;font-weight:bold"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;At this point, I wonder if I should be building small examples of perspective and transform to improve my understanding, &lt;strong&gt;or&lt;/strong&gt; if I should move forward with other ideas for improving the animation. Tough choices. I’ll report back either way.&lt;/p&gt;

&lt;h2 id="update-dec-4-2017"&gt;Update Dec 4 2017:&lt;/h2&gt;

&lt;p&gt;I improved the performance of the prototype. You can &lt;a href="http://www.nickivance.com/flipside/index.html"&gt;play with it online&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20171115_laws-of-repulsion/</id>
    <title>Laws Of Repulsion: Building A Herding Dog Game</title>
    <published>2017-11-15T00:00:00Z</published>
    <updated>2017-11-15T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20171115_laws-of-repulsion/" type="text/html"/>
    <content type="html">&lt;h1 id="laws-of-repulsion-building-a-herding-dog-game"&gt;Laws Of Repulsion: Building A Herding Dog Game&lt;/h1&gt;

&lt;p&gt;My latest project has been the basic game mechanics for herding humans with a dog. Why herd humans with a dog? You’ll find out if I ever finish this game. I don’t want to spoil it in advance.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20171115_laws-of-repulsion/human-dog.png" alt=""&gt;
&lt;/figure&gt;

&lt;p&gt;My partner and I started this project earlier this year. In one short pair programming session, we built a dog (a box with a nose) that would animate to wherever you clicked or tapped on the screen.&lt;/p&gt;

&lt;iframe src="https://player.vimeo.com/video/242907307" width="640" height="561" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;p&gt;My goal has been to get a simple version of the game play working that I can evaluate whether it’s fun to herd humans with a dog and thus whether the rest of the game is worth building.&lt;/p&gt;

&lt;h2 id="its-simple-right"&gt;It’s simple, right?&lt;/h2&gt;
&lt;p&gt;Here are the basic steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Draw a human (another box with a nose).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Tell the human to walk in a straight line.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Tell the human to turn 30 degrees when the human is within 10 pixels of the dog (a single corner of the dog, not anywhere on a rectangle, to keep things simple).&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then stop at this point because so far this sucks. The human spins like crazy when the dog is nearby because the human spins faster than it can move away from the dog.&lt;/p&gt;

&lt;p&gt;[Sorry, I don’t have a video of this. I must’ve deleted the code before I got too dizzy.]&lt;/p&gt;

&lt;h2 id="side-tangent-about-the-programming-im-learning"&gt;Side tangent about the programming I’m learning&lt;/h2&gt;
&lt;p&gt;Despite the stupid spinning human, I’m happy because I’d learned how to use threading macros in Clojure and was getting familiar with them.&lt;/p&gt;

&lt;p&gt;A threading macro is a nicer way to write out a set of nested instructions. Here’s a familiar example from math: &lt;code&gt;((3 * 2) + 1)&lt;/code&gt; is one math expression nested inside another. You start with evaluating &lt;code&gt;(3 * 2)&lt;/code&gt; and then you take the result and add it to 1.&lt;/p&gt;

&lt;p&gt;A threading macro writes this in the order it will be evaluated:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-&amp;gt; (3 * 2)

(+ 1)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;At first, I couldn’t see how these chained together or the order of the arguments. Is it &lt;code&gt;((3 * 2) + 1)&lt;/code&gt; or &lt;code&gt;(1 + (3 * 2))&lt;/code&gt;? There is a rule that determines this and you just have to learn it. Not so bad. Very useful.&lt;/p&gt;

&lt;p&gt;[If you’re interested in Clojure, you can read about it in this article about threading macros. I didn’t show clojure code here because I think it would confuse most people I know – standard math notation is more familiar.]&lt;/p&gt;

&lt;h2 id="building-a-grid"&gt;Building a grid&lt;/h2&gt;
&lt;p&gt;I was a bit stumped about the spinning human.&lt;/p&gt;

&lt;p&gt;I needed a system that would give the human a new direction only once, not repeatedly. I was stuck trying to imagine how to do this with the proximity check I was already using.&lt;/p&gt;

&lt;p&gt;My partner suggested that I build a grid system. So I thought about it for a bit and it began to appeal to me. I did a little reading about grids from Amit Patel. (Someday I want to try a hex grid. Amit has great tutorials about building these.)&lt;/p&gt;

&lt;p&gt;Since I’m working with a web browser, I’m already using the pixel grid. But it’s been difficult because my characters are larger than a pixel; so which of its many pixels defines a character’s location? Keeping track of those pixels gets complicated.&lt;/p&gt;

&lt;p&gt;By building my own grid, I can group the pixels together, say in 40px by 40px boxes, and my characters can be located &lt;em&gt;within&lt;/em&gt; the box. Now, the location of my human is simply a pair of coordinates on the grid (similar to the x,y coordinates of graphing equations). It’s much simpler.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20171115_laws-of-repulsion/human-dog-grid.png" alt=""&gt;
&lt;/figure&gt;

&lt;p&gt;Having a visible grid made it easier for me to debug because I could see when things weren’t lined up right.&lt;/p&gt;

&lt;h2 id="moving-on-a-grid-sensing-walls-and-the-dog"&gt;Moving on a grid, sensing walls and the dog&lt;/h2&gt;
&lt;p&gt;Once I had the human and dog on the grid, I wanted the human to move about the grid without input from me.&lt;/p&gt;

&lt;p&gt;First, I had the human move straight down the grid and stop when it reached the walls, the edges of the grid.&lt;/p&gt;

&lt;p&gt;Then I added a check for the dog. If the dog was in the way, the human would stop.&lt;/p&gt;

&lt;p&gt;Just moving straight down the screen is quite boring but it’s a necessary step towards something more interesting.&lt;/p&gt;

&lt;h2 id="semi-independent-movement"&gt;Semi-independent movement&lt;/h2&gt;
&lt;p&gt;There’s no element of surprise in what I’d built so far. The human is so predictable and all you can do as the dog is stand in its way.&lt;/p&gt;

&lt;p&gt;Someday, I want the human to have a path it is trying to follow but to start simple, I’m just going to have the human move around so I can try and herd it with the dog.&lt;/p&gt;

&lt;p&gt;Movement 1: Look at the four boxes adjacent to the human, discard any that are off the grid or that contain the dog, then randomly choose one to move into.&lt;/p&gt;

&lt;iframe src="https://player.vimeo.com/video/242905870" width="640" height="536" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;p&gt;The human walked all over the place, willy nilly. How do you herd something like that?&lt;/p&gt;

&lt;p&gt;Movement 2: You are given a direction, move in that direction until you reach a wall or the dog, then turn to a randomly chosen direction.&lt;/p&gt;

&lt;iframe src="https://player.vimeo.com/video/242906060" width="640" height="646" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;p&gt;This worked okay but my human tended to walk around mostly along the edges of the grid. I want the human to cross the middle sometimes.&lt;/p&gt;

&lt;p&gt;Movement 3: Toss a 5 sided die and if it’s a 1 (20% chance) then change direction, otherwise, follow the instructions of Movement 2.&lt;/p&gt;

&lt;iframe src="https://player.vimeo.com/video/242906037" width="640" height="646" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;p&gt;Pretty passable. The human is a bit tricky to herd but the feeling of the interaction is beginning to take shape.&lt;/p&gt;

&lt;h2 id="laws-of-repulsion"&gt;Laws of repulsion&lt;/h2&gt;
&lt;p&gt;I want to simulate the way herding dogs can push other animals (or humans) in a certain direction.&lt;/p&gt;

&lt;p&gt;The human shouldn’t want to cross in front of the dog. It might be really cool to do some kind of reverse gravity stuff here but I need to start with something simpler. A dog-zone.&lt;/p&gt;

&lt;p&gt;I created a line of boxes in front of the dog that are considered the dog-zone, a place that causes the human to change direction.&lt;/p&gt;

&lt;iframe src="https://player.vimeo.com/video/242906073" width="640" height="649" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;p&gt;Now, the dog has more reach, enough to do some basic herding. With some imagination.&lt;/p&gt;

&lt;h2 id="next-up"&gt;Next up&lt;/h2&gt;
&lt;p&gt;This is a good, basic prototype for dog herding. Prototypes should be bare bones, the least effort to get the most learning. I had bigger ideas but this does an excellent job of testing out a very simple version of the most important aspect of this game.&lt;/p&gt;

&lt;p&gt;Is it fun to herd humans?
I don’t think this prototype gives me a definitive “yes” but it doesn’t tell me “no”.&lt;/p&gt;

&lt;p&gt;I’m going to take a break from this and work on something else. The grid work in this project will probably be really useful for other games I want to build. A little time away will help me decide what to prototype next for dog herding.&lt;/p&gt;

&lt;p&gt;Thanks for reading! Let me know if you have any questions or ideas for what to try next with the dog herding.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20171104_so-fresh/</id>
    <title>The So Fresh 9000: A washing machine interface built with state charts</title>
    <published>2017-11-04T00:00:00Z</published>
    <updated>2017-11-04T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20171104_so-fresh/" type="text/html"/>
    <content type="html">&lt;h1 id="the-so-fresh-9000-br-a-washing-machine-interface-built-with-state-charts"&gt;The So Fresh 9000: &lt;br&gt; A washing machine interface built with state charts&lt;/h1&gt;

&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;I had a bad experience with a washing machine so I tried building my own using a state machine. The project has convinced me that I want to keep doing product design with state machines and state charts.&lt;/p&gt;

&lt;h2 id="the-cabrio-that-never-stops"&gt;The Cabrio that never stops&lt;/h2&gt;

&lt;p&gt;A year ago, I was trying to wash the linens at a friend’s vacation home before I left for the airport. I was using Whirlpool’s Cabrio WTW7300DW.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20171104_so-fresh/cabrio_interface.png" alt=""&gt;
  &lt;figcaption&gt;Not sure what the green, red, and blue lights in the center mean.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;With seven minutes left on the wash, the Cabrio got stuck in a loop. Each time I went back to check on it, it still showed seven minutes and the linens were spinning in water, far too wet to put in the dryer. Doesn’t this machine have a spinning teacups mode for extracting as much water as possible?&lt;/p&gt;

&lt;p&gt;Obviously there’s an error but there’s nothing telling me what’s wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem #1:&lt;/strong&gt; no user feedback for an error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem #2:&lt;/strong&gt; the error is looping. Why is there a state with no exit?&lt;/p&gt;

&lt;p&gt;My options are just to pause the cycle and try to run another cycle. Preferably just the spin cycle.
&lt;strong&gt;Problem #3:&lt;/strong&gt; I didn’t notice the option to Rinse &amp;amp; Spin.&lt;/p&gt;

&lt;p&gt;On the interface, this is a small label above something labeled Utility Tools. Is it an option I can select? I’m not sure.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20171104_so-fresh/cabrio_utilitytools.png" alt=""&gt;
  &lt;figcaption&gt;Tools that are so full of utility.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I stumbled on the solution. I shuffled the linens around in the wash tub and once I started the machine again, the time remaining began to count down. Turns out the laundry was unbalanced and for some reason that was preventing the cycle from finishing.&lt;/p&gt;

&lt;p&gt;The machine is top-loaded so the laundry sits in a vertically-oriented cylinder that is spun by a motor. If the laundry is unevenly distributed, more weight on one side of the cylinder than the other, then the cylinder will wobble as it spins. An unbalanced laundry tub can cause wear on the motor and its connections to the tub. It can also makes disturbing thumping noises.&lt;/p&gt;

&lt;p&gt;I’m not sure why the unbalanced load caused the machine to loop. Seems like it would do more damage that way.&lt;/p&gt;

&lt;p&gt;Anyway, this is how the story starts. I was worried about either missing a flight or creating a moldy laundry monster for the next vacationers. It was a crappy product experience.&lt;/p&gt;

&lt;p&gt;But I don’t want to just throw shade, I decided to try to make something better.&lt;/p&gt;

&lt;h2 id="the-problem-to-solve"&gt;The problem to solve&lt;/h2&gt;
&lt;p&gt;My goal was to build a washing machine interface that:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;allows users to choose their wash preferences or use presets&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;runs the wash based on the user inputs&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;displays the time remaining&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;displays an error when the machine is unbalanced&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to these basic goals, I included the usual things you expect from a washing machine. For example, if you open the door, it should pause which means that it remembers what it was doing and resumes when you close the door. Give it an elephant’s memory. I won’t list the rest of these details here.&lt;/p&gt;

&lt;h2 id="the-solution"&gt;The solution&lt;/h2&gt;
&lt;p&gt;The solution could’ve been much simpler. I could’ve just drawn you a picture or even marked up a photo of the current interface to say “Add an error message here when the sensor registers an unbalanced load!”&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20171104_so-fresh/cabrio_adderror.png" alt=""&gt;
&lt;/figure&gt;

&lt;p&gt;I wanted to work through designing the whole system to see how things like settings and sensors on the machine could work together to present a clear, usable interface.&lt;/p&gt;

&lt;p&gt;So I built a web-based prototype of the interface that I could interact with and move through its various states.&lt;/p&gt;

&lt;iframe src="https://player.vimeo.com/video/247090894" width="640" height="316" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;p&gt;For unbalanced loads, I’ve simply made an unbalanced load message appear on the interface that is cleared if the balance is regained. I’m just working with the interface here so I’ve made assumptions about the physical sensors and how frequently they report back to the washing machine’s control system.&lt;/p&gt;

&lt;h2 id="how-its-made"&gt;How it’s made&lt;/h2&gt;

&lt;p&gt;The washing machine prototype is basically a functioning website.&lt;/p&gt;

&lt;p&gt;The core of the program is a state machine. The state machine is terrifically useful because it controls the app by moving between states. For example, when the washing machine is inactive, it’s in the Dim-Display state. I can tell the interface to dim the display when the washing machine is in this state.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20171104_so-fresh/cabrio_adderror.png" alt=""&gt;
&lt;/figure&gt;

&lt;p&gt;Then, when someone presses the Start button, the machine knows to transition from the Dim-Display state into the Ready state.&lt;/p&gt;

&lt;p&gt;The awesome thing about state machines is that you create a system of logic for your program to follow. Based on this state machine in this chart, there is no way to move from the Ready state to the Done state. So in creating the machine, I can declare the behavior I want and prevent behavior I don’t want.&lt;/p&gt;

&lt;p&gt;I built the state machine with a tool my partner, &lt;a href="https://kevinlynagh.com/"&gt;Kevin&lt;/a&gt;, has been working on. He hasn’t shared anything about it publicly yet so just ask if you’re curious.&lt;/p&gt;

&lt;p&gt;I was able to define the state machine in code which generates an interactive chart. Being able to see the chart and navigate it helps me test out the system I’ve created. I can explore the possibilities I’ve created and look for gaps in functionality or behavior I want to prevent.&lt;/p&gt;

&lt;p&gt;In addition to defining the state machine, I can code up an interface that responds to events in the system. That’s what you saw above.&lt;/p&gt;

&lt;h2 id="statecharts-make-design-better"&gt;Statecharts make design better&lt;/h2&gt;
&lt;p&gt;My process for designing interfaces used to start with&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;a bit of writing – goals, use cases, a list of screens I think I need to draw,&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;an information architecture diagram,&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;sketching out the screens,&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;and so on.&lt;/p&gt;

&lt;p&gt;None of these tools does a good job of conveying the underlying functionality of the system.&lt;/p&gt;

&lt;p&gt;The information architecture is the closest I used to come to looking at the system from a high level. And to be honest, an information architecture is a poor representation of a program. It’s a rough approximation of how websites used to be built using just HTML and CSS, before programs on servers began generating the HTML, CSS, and Javascript that web browsers read. Information architecture is best used for communicating how a website will be organized.&lt;/p&gt;

&lt;p&gt;With a state chart I can define the control system that affects how people interact with what I’m designing.&lt;/p&gt;

&lt;p&gt;I’m curious to try this out with other folks. I hope state machines will make it easier for the teams I work with to align and get a prototype working quickly.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20170721_boat-animation/</id>
    <title>Animating with Quicktime and nudging objects</title>
    <published>2017-10-23T00:00:00Z</published>
    <updated>2017-10-23T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20170721_boat-animation/" type="text/html"/>
    <content type="html">&lt;h1 id="animating-with-quicktime-and-nudging-objects"&gt;Animating with Quicktime and nudging objects&lt;/h1&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20170721_boat-animation/boat_wake.gif" alt=""&gt;
&lt;/figure&gt;

&lt;p&gt;I wanted to make this animation based on a handkerchief I saw in a shop window.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20170721_boat-animation/boat-inspiration.jpg" alt=""&gt;
&lt;/figure&gt;

&lt;p&gt;I recreated the basic shapes in Sketch, a vector drawing tool. At first, I thought I’d do this in the stop-animation style: draw each frame and join the frame pngs into a gif.&lt;/p&gt;

&lt;p&gt;You know what’s easier? Build one image and film yourself nudging it across the screen.&lt;/p&gt;

&lt;p&gt;There are a lot of tools that take a video file and output a gif. From what I’ve read and proved with my own experiments, gifs are actually larger than video files. A gif is an image file format. But a lot of websites where you might want to post your animation don’t support video upload, only image upload. I’ve heard this is because there are security concerns but I haven’t investigated.&lt;/p&gt;

&lt;p&gt;This only took me 2 hours, start to finish. I’d like to try it again in the future. It would be nice if the boat could go at an angle to the water lines. And to create the little wave that boats push out in front of them. Or to generate this all programmatically. Next time! :)&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20171023_designing-interactive-tables/</id>
    <title>Designing interactive tables: keyboard navigation and selection</title>
    <published>2017-10-23T00:00:00Z</published>
    <updated>2017-10-23T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20171023_designing-interactive-tables/" type="text/html"/>
    <content type="html">&lt;h1 id="designing-interactive-tables-br-keyboard-navigation-and-selection"&gt;Designing interactive tables: &lt;br&gt; keyboard navigation and selection&lt;/h1&gt;

&lt;p&gt;Interactive tables are an extremely useful feature of many digital products. Think Excel or Google Spreadsheets, Gmail, Quicken, Jira, and many business tools. People use these programs at home and on the job to review and operate on large amounts of data.&lt;/p&gt;

&lt;p&gt;This article covers some considerations I encountered when designing an interactive table that supported both mouse and keyboard inputs. Hopefully, it’s helpful to those of you working on a project that includes interactive tables.&lt;/p&gt;

&lt;h2 id="lets-start-with-a-short-definition-of-an-interactive-table"&gt;Let’s start with a short definition of an interactive table&lt;/h2&gt;

&lt;p&gt;Excel is a classic example of an interactive table. It’s a tool for editing and organizing data. You can select data, copy it, apply functions, and so much more.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20171023_designing-interactive-tables/spreadsheet.png" alt=""&gt;
  &lt;figcaption&gt;A screenshot of Google Spreadsheets because it's close enough for our purposes but I talk about Excel here because it's the original program I learned from.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;My experience with interactive tables comes from designing a personal finance app, Moneyhawk. Central to the app is a table of bank transactions. People who use Moneyhawk need to be able to edit data in one or more of their transactions.&lt;/p&gt;

&lt;p&gt;For example, I want people to be able to change the payee on a dozen bank transactions from “SQ 34COF#222” to something more readable like, “34th St Coffee”.&lt;/p&gt;

&lt;iframe allowfullscreen="" frameborder="0" height="564" mozallowfullscreen="" src="https://player.vimeo.com/video/246649490" webkitallowfullscreen="" width="640"&gt;&lt;/iframe&gt;

&lt;h2 id="weve-got-a-goal-how-do-we-get-there"&gt;We’ve got a goal, how do we get there?&lt;/h2&gt;

&lt;p&gt;I started by breaking down the desired functionality into basic actions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;select a transaction&lt;/li&gt;
  &lt;li&gt;select multiple transactions&lt;/li&gt;
  &lt;li&gt;edit selected transaction(s)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Are the multiple transactions all adjacent or here-and-there? Let’s split the multiple transactions selection into two actions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;select multiple transactions adjacent to each other&lt;/li&gt;
  &lt;li&gt;select multiple transactions spread out through the list&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What if someone wants to clear their selection? Or select everything? (The latter is especially likely if someone used search to filter the list to the set of transactions they want to change.)&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;select all transactions&lt;/li&gt;
  &lt;li&gt;deselect a transaction&lt;/li&gt;
  &lt;li&gt;deselect all transactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here you can see the selection and deselection my partner and I built:&lt;/p&gt;

&lt;iframe allowfullscreen="" frameborder="0" height="564" mozallowfullscreen="" src="https://player.vimeo.com/video/246649640" webkitallowfullscreen="" width="640"&gt;&lt;/iframe&gt;

&lt;p&gt;No surprises, right? These interactions should look familiar.&lt;/p&gt;

&lt;p&gt;The design here was mostly about reusing the same kinds of interactions we see in apps like Finder and other file management programs. Click on a row to select it. Click on another row and the previous selection is cleared and the target row is selected. We use keys to modify the click’s action. &lt;em&gt;Shift + click&lt;/em&gt; selects everything between a selected row (which one if multiple are selected?) and the target row. &lt;em&gt;Command + click&lt;/em&gt; adds toggles the selection of the target row.&lt;/p&gt;

&lt;p&gt;I don’t want these few paragraphs and a quick video to imply that building this was trivial. Discussing the intricacies of designing click functionality is beyond the scope of this article. To give you a peek, here are a few considerations:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;What if someone double clicks? Is that the same as a click?&lt;/li&gt;
  &lt;li&gt;What if someone clicks and holds the click for a long time? What is their intention?&lt;/li&gt;
  &lt;li&gt;Do you want a hover state for the objects you can click on?&lt;/li&gt;
  &lt;li&gt;What should the cursor look like?&lt;/li&gt;
  &lt;li&gt;Is there an active state that changes the appearance when the user clicks down? Or when they release the click? What meaning do these visual changes convey?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="designing-for-keyboard-input"&gt;Designing for keyboard input&lt;/h2&gt;

&lt;p&gt;Ok, we’re pretty happy with what we’ve got above. But how do we do the same actions with a keyboard? How do we use a keyboard to tell a computer that we want &lt;em&gt;a&lt;/em&gt;,&lt;em&gt;b&lt;/em&gt;, and &lt;em&gt;e&lt;/em&gt; transactions selected and we want to edit their payee?&lt;/p&gt;

&lt;p&gt;Maybe this question seems too simple. Of course we use a cursor or a pointer of some kind! I actually feel hesitant about this answer because using a cursor feels too obvious and &lt;strong&gt;obvious answers hamper ingenuity.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But I don’t want to push the envelope with keyboard selection because it’s worthwhile to work with pre-existing systems when they are widely familiar.&lt;/p&gt;

&lt;p&gt;Many, many people are accustomed to using cursors for text interfaces. The text interface is a series of tiny cells. Each cell can hold a single character and so we move our cursor left, right, up, down to point at an empty cell or a character we want to change.&lt;/p&gt;

&lt;figure&gt;
  &lt;iframe allowfullscreen="" frameborder="0" height="564" mozallowfullscreen="" src="https://player.vimeo.com/video/246649497" webkitallowfullscreen="" width="640"&gt;&lt;/iframe&gt;
  &lt;figcaption&gt;In this text editor, there are 432 characters and just as many destinations for our cursor.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Ok, that’s a good start. We can use arrows to move a cursor around our screen so we can point at stuff. But it can be really slow to move your cursor through thousands of characters to make edits. A table helps us create chunks of information that we can organize to be more easily navigated and discovered.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20171023_designing-interactive-tables/spreadsheet_chunks.png" alt=""&gt;
  &lt;figcaption&gt;In a this table, our data is in chunks. Instead of navigating the 180 characters in this table, we can navigate the 60 cells.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Excel offers us a table to chunk information into cells. Each cell of the table is its own text interface that can do incredible functions and equations, and store images and masses of text. Heck, these little text interfaces can even reference each other!&lt;/p&gt;

&lt;p&gt;When you move around in Excel, either with a mouse or keyboard, a border around the cell matches your movements. Like a text cursor, this cell border is the &lt;strong&gt;pointer&lt;/strong&gt; that indicates your current &lt;strong&gt;target&lt;/strong&gt; for any actions. When you click on a cell, the pointer moves at the same time that the action occurs, relegating the pointer to a piece of feedback.&lt;/p&gt;

&lt;figure&gt;
  &lt;iframe allowfullscreen="" frameborder="0" height="564" mozallowfullscreen="" src="https://player.vimeo.com/video/246649507" webkitallowfullscreen="" width="640"&gt;&lt;/iframe&gt;
  &lt;figcaption&gt;Look at how fast we can zoom around this screen!&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="pointing-at-rows-or-cells"&gt;Pointing at rows or cells?&lt;/h2&gt;

&lt;p&gt;In Moneyhawk, I chose to have row pointers instead of cell pointers for two reasons:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Transactions are more significant objects than their parts. I chose to emphasize transactions.&lt;/li&gt;
  &lt;li&gt;I wanted to limit the number of decisions someone has to make when they’re using a keyboard. This helps with speed and reduces cognitive load.&lt;/li&gt;
&lt;/ol&gt;

&lt;figure&gt;
  &lt;iframe allowfullscreen="" frameborder="0" height="564" mozallowfullscreen="" src="https://player.vimeo.com/video/246649501" webkitallowfullscreen="" width="640"&gt;&lt;/iframe&gt;
  &lt;figcaption&gt;Moneyhawk's pointer is the blue row outline. A selected row has a blue background to differentiate from the pointer.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2 id="tradeoffs-between-navigation-and-memorable-keyboard-commands"&gt;Tradeoffs between navigation and memorable keyboard commands&lt;/h2&gt;

&lt;p&gt;Instead of navigating to a cell and hitting an edit command, you navigate to the row and hit one of 4 edit commands that correspond to the cell, or field, that you want to edit. There are 3 extra edit commands to remember (hopefully eased by tying them to the first letter of the field name – &lt;em&gt;cmd + p&lt;/em&gt; for editing payee) but there are only &lt;em&gt;n&lt;/em&gt; rows to navigate instead of &lt;em&gt;n&lt;/em&gt; * 4 cells.&lt;/p&gt;

&lt;p&gt;I wouldn’t suggest that Excel take this route because the promise of Excel is flexibility. You can assign any meaning you want to the rows and columns.&lt;/p&gt;

&lt;p&gt;Here’s the keyboard command list for Moneyhawk:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Command&lt;/th&gt;
      &lt;th&gt;Action&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;arrow keys&lt;/td&gt;
      &lt;td&gt;move pointer up/down a row&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Spacebar&lt;/td&gt;
      &lt;td&gt;select a transaction, select multiple transactions, deselect a tranasction&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CMD + a&lt;/td&gt;
      &lt;td&gt;select all transactions&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Escape&lt;/td&gt;
      &lt;td&gt;deselect all transactions&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CMD + p&lt;/td&gt;
      &lt;td&gt;edit payee of selected transaction(s)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CMD + t&lt;/td&gt;
      &lt;td&gt;edit tag(s) of selected transaction(s)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CMD + k&lt;/td&gt;
      &lt;td&gt;edit category(ies) of selected transaction(s)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CMD + n&lt;/td&gt;
      &lt;td&gt;edit notes of selected transaction(s)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CMD + c&lt;/td&gt;
      &lt;td&gt;copy selected transactions&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Note: The spacebar handles most of the selection and deselection actions based on where the pointer points. The keyboard command for selecting or deselecting a transaction is actually a combination of these two actions: toggle selection.&lt;/p&gt;

&lt;p&gt;My partner and I felt strongly about including keyboard commands so we actually built them first, before adding the mouse functionality. I recommend building the keyboard commands out first because it’s easier to map mouse actions to keyboard actions than vice versa.&lt;/p&gt;

&lt;h2 id="another-example-of-a-keyboard-interface"&gt;Another example of a keyboard interface&lt;/h2&gt;

&lt;p&gt;I want to give a shout out to Gmail which served as an example for me in my design work. You may not know this, but you can navigate pretty quickly through &lt;a href="https://support.google.com/mail/answer/6594?co=GENIE.Platform%3DDesktop&amp;amp;hl=en"&gt;Gmail with keyboard commands&lt;/a&gt;. The pointer in this system is a little blue bar on the left edge of the email bar.&lt;/p&gt;

&lt;figure style="max-width: 287px"&gt;
  &lt;img src="https://nickivance.com/writing/20171023_designing-interactive-tables/gmail_pointer.png" alt=""&gt;
  &lt;figcaption&gt;Admit it, you still email yourself sometimes.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Sometimes it disappears but that’s a story for another post. Let me know if you have a guess or a certainty why that blue pointer disappears.&lt;/p&gt;

&lt;p&gt;Do you have any favorite keyboard-based interfaces? Please share!&lt;/p&gt;

&lt;h2 id="in-conclusion"&gt;In conclusion…&lt;/h2&gt;

&lt;p&gt;Hopefully, you’ve found some useful tidbits in here for the next time you work on a keyboard interface or an interactive table.&lt;/p&gt;

&lt;p&gt;Thanks for reading. If you think this is worth sharing, I’d be grateful if you would. Cheers!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20171011_melbourne-move/</id>
    <title>Living in Melbourne</title>
    <published>2017-10-11T00:00:00Z</published>
    <updated>2017-10-11T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20171011_melbourne-move/" type="text/html"/>
    <content type="html">&lt;h1 id="living-in-melbourne"&gt;Living in Melbourne&lt;/h1&gt;

&lt;p&gt;At the beginning of October, my partner and I began an extended stay in Australia. We’ll be in Melbourne for the next bit.&lt;/p&gt;

&lt;p&gt;So far, I can say that I love how Australians shorten nouns into diminutives. Especially when I see it in formal contexts.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20171011_melbourne-move/posties.png" alt=""&gt;

  &lt;figcaption&gt;The Australian Post career opportunities list "Postie" as an admirably gender neutral version of "postal delivery worker".&lt;/figcaption&gt;

&lt;/figure&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20171009_fireplace/</id>
    <title>A Small Flickering Fireplace</title>
    <published>2017-10-09T00:00:00Z</published>
    <updated>2017-10-09T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20171009_fireplace/" type="text/html"/>
    <content type="html">&lt;h1 id="a-small-flickering-fireplace"&gt;A Small Flickering Fireplace&lt;/h1&gt;

&lt;figure style="max-width: 555px; display: flex"&gt;

  &lt;img src="https://nickivance.com/writing/20171009_fireplace/fireplace_animation.gif" alt="" style="width: auto"&gt;

&lt;/figure&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20171006_moneyhawk/</id>
    <title>Moneyhawk App: Organize your money without trusting the cloud</title>
    <published>2017-10-06T00:00:00Z</published>
    <updated>2017-10-06T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20171006_moneyhawk/" type="text/html"/>
    <content type="html">&lt;h1 id="moneyhawk-app-br-organize-your-money-without-trusting-the-cloud"&gt;Moneyhawk App: &lt;br&gt; Organize Your Money Without Trusting The Cloud&lt;/h1&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20171006_moneyhawk/hero.png" alt=""&gt;
&lt;/figure&gt;

&lt;div class="info"&gt;
  &lt;div class="info-header"&gt;
    &lt;p class="info-title"&gt;Update&lt;/p&gt;
    &lt;p class="info-date"&gt;December 21, 2025&lt;/p&gt;
  &lt;/div&gt;
  &lt;p&gt;This project has been retired.&lt;/p&gt;
  &lt;p&gt;I still use it myself but it's not under development or publicly available.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;My experience with interactive tables comes from designing a personal finance app, Moneyhawk. Central to the app is a table of bank transactions. People who use Moneyhawk need to be able to edit data in one or more of their transactions.
Features include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;cloudless, your data is stored on your computer&lt;/li&gt;
  &lt;li&gt;drag ‘n drop file import&lt;/li&gt;
  &lt;li&gt;view totals for categories&lt;/li&gt;
  &lt;li&gt;create tags, categories, and notes&lt;/li&gt;
  &lt;li&gt;rename transaction payee&lt;/li&gt;
  &lt;li&gt;fast search&lt;/li&gt;
  &lt;li&gt;date and account filtering&lt;/li&gt;
  &lt;li&gt;keyboard commands&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post covers the thinking behind a couple of our key features: data safety, spending categories, and performance. To create these features, we’ve made some unconventional trade-offs such as eschewing auto-download bank sync, mobile or web apps, and budgets.&lt;/p&gt;

&lt;h2 id="tracking-instead-of-budgets"&gt;Tracking instead of budgets&lt;/h2&gt;

&lt;p&gt;In Moneyhawk, we’ve built out the tools to categorize transactions for tracking but we haven’t added a way to create a budget. Should a budget number persist for each future month? Should a budget be for calendar months or any time period you choose?
For example, I want people to be able to change the payee on a dozen bank transactions from “SQ 34COF#222” to something more readable like, “34th St Coffee”.&lt;/p&gt;

&lt;figure style="max-width: 555px; display: flex"&gt;
  &lt;img src="https://nickivance.com/writing/20171006_moneyhawk/categories.png" alt="" style="width: auto"&gt;
&lt;/figure&gt;

&lt;p&gt;Are budgets as accurate as they promise to be? Do you always end up with the final balance at the end of the month that you expected from following your budget? What happens when reimbursements are delayed for months? When bills are divided amongst housemates?&lt;/p&gt;

&lt;p&gt;There are other great tools out there for budgeting (YNAB, for example), so we’re satisfied with tracking spending for now.&lt;/p&gt;

&lt;h2 id="data-safety-and-privacy"&gt;Data safety and privacy&lt;/h2&gt;

&lt;p&gt;It feels like every day another large company is hacked and our sensitive data is compromised. But there aren’t many options on the market that protect our data. Many promise high levels of encryption on servers and clients and pieces of the puzzle we’ve never heard of before.&lt;/p&gt;

&lt;p&gt;We chose a simpler solution - we don’t transfer or store your data. You have full control.&lt;/p&gt;

&lt;p&gt;Having full control over your data with Moneyhawk means that:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;you never have to trust us with your bank passwords&lt;/li&gt;
  &lt;li&gt;you store your bank data locally on your computer, not in a cloud server that could be hacked&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tradeoff with our approach is that you need to download your data from your bank and import it into Moneyhawk, unlike the many automated services out there today. We feel data security is worth the effort.&lt;/p&gt;

&lt;h2 id="performance"&gt;Performance&lt;/h2&gt;
&lt;p&gt;Our goal is to make Moneyhawk fast and reliable. A tool you can grow comfortable using.&lt;/p&gt;

&lt;p&gt;One aspect of being fast is being responsive. Check out the search. It matches your keystrokes immediately.&lt;/p&gt;

&lt;iframe src="https://player.vimeo.com/video/237043379" width="640" height="383" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;p&gt;Another aspect of being fast is having keyboard commands. Muscle memory wins out over mousing around. Here’s a few of the commands:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Tab to navigate between search and the transaction list&lt;/li&gt;
  &lt;li&gt;CMD + p to edit payee&lt;/li&gt;
  &lt;li&gt;CMD + t to edit tags&lt;/li&gt;
  &lt;li&gt;CMD + k to edit categories (didn’t want to get in the way of CMD + c for copy)&lt;/li&gt;
  &lt;li&gt;CMD + n to edit notes&lt;/li&gt;
  &lt;li&gt;Enter to save&lt;/li&gt;
  &lt;li&gt;Shift + Enter to save &amp;amp; edit the next transaction for the same field&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reliability means that Moneyhawk won’t spaz out and lose the data you just entered.&lt;/p&gt;

&lt;p&gt;A side benefit of our security approach is that we don’t have to worry about the difficulties of syncing between your bank, our cloud server, and the local app on your computer. Large companies with armies of developers are still working on making the syncing experience seamless.&lt;/p&gt;

&lt;p&gt;You might’ve guessed we’re fans of old-school desktop software.&lt;/p&gt;

&lt;h2 id="whats-next"&gt;What’s next?&lt;/h2&gt;

&lt;p&gt;We have a few things in progress.&lt;/p&gt;

&lt;p&gt;Totals can be confusing. Right now, we sum debits, credits, and all transactions. There are two problems to solve:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;transfers give the appearance of higher spending and income - did you really “spend” $100 if you just moved it between accounts?&lt;/li&gt;
  &lt;li&gt;an overall total could match the sum of your bank balances but only if you’ve imported your full transaction history (because some banks may not keep the full history, we may need to introduce an “initial balance”)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Date filtering for custom time frames.&lt;/p&gt;

&lt;p&gt;Better design documentation. This one’s a behind-the-scenes feature. You might never see it but it will help us build a better Moneyhawk. For example, we’re building a nice state chart for our subscription page so we can make sure the app moves smoothly between states (new, pending, subscribed, cancelled) and won’t get stuck in an undefined black hole.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;&lt;del&gt;You can download the app for free at &lt;a href="http://moneyhawkapp.com/"&gt;moneyhawkapp.com&lt;/a&gt;.&lt;/del&gt; (We’ve stopped hosting the website.)&lt;/p&gt;

&lt;p&gt;To clear skies,&lt;/p&gt;

&lt;p&gt;Nicki&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20170811_stools-part-two/</id>
    <title>Sitting Stools - Part 2</title>
    <published>2017-08-11T00:00:00Z</published>
    <updated>2017-08-11T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20170811_stools-part-two/" type="text/html"/>
    <content type="html">&lt;h1 id="sitting-stools-br-part-2"&gt;Sitting Stools &lt;br&gt; Part 2&lt;/h1&gt;

&lt;p&gt;The July sitting stool project has wrapped up. I made a cement-top stool and three A-frame stools from Ikea bed slats. To top it off, we had a little party with friends, some of whom showed off their own sitting stools they’d made for the occasion.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20170811_stools-part-two/stools.jpg" alt=""&gt;

  &lt;figcaption&gt;Professionals made the ones on the far left, unfortunately they're hard to see in this photo. The furry one was easily most comfortable and personifiable.&lt;/figcaption&gt;

&lt;/figure&gt;

&lt;p&gt;In the photo, the second stool from the right is the cement-top design I learned from this awesome &lt;a href="https://www.google.com/url?sa=t&amp;amp;rct=j&amp;amp;q=&amp;amp;esrc=s&amp;amp;source=web&amp;amp;cd=3&amp;amp;ved=0ahUKEwjxhfiqldDVAhVC5GMKHYF9DoIQtwII9gEwAg&amp;amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DnpPRWDgpduI&amp;amp;usg=AFQjCNGkcD_u0wE_K8UhbMYkh96xBiyGXw"&gt;how-to video from HomeMade Modern&lt;/a&gt;. I tried three times to build a cement stool. Only one survived.&lt;/p&gt;

&lt;p&gt;The tricky thing about cement is landing the mixture and how we humans try to describe consistency. I read that the cement should feel like cookie dough when you try to stir it. But cookies don’t have rock aggregate in them so…. My partner recommended peanut butter consistency. That seemed to work for me but at the party we discovered that we all disagreed on whether that meant chunky or smooth peanut butter. What I do know is that if you have too much water, the extra evaporation of that water can cause cracks, which is what I think did in my other two stool attempts.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20170811_stools-part-two/cement_stool.jpg" alt=""&gt;

  &lt;figcaption&gt;This is attempt #1. If you look closely, you can see three cracks. Luckily the dowels came out so I was able to reuse them.&lt;/figcaption&gt;

&lt;/figure&gt;

&lt;p&gt;The whole thing was a big success. I was really thrilled with my fine friends who all brought really amazing sitting stools.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20170719_smash-banana-part-two/</id>
    <title>Smash Banana - Part 2</title>
    <published>2017-07-19T00:00:00Z</published>
    <updated>2017-07-19T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20170719_smash-banana-part-two/" type="text/html"/>
    <content type="html">&lt;h1 id="smash-banana-br-part-2"&gt;Smash Banana &lt;br&gt; Part 2&lt;/h1&gt;

&lt;p&gt;The game is going great. The obstacles are bananas that are flung off screen and the car is looking like my car. There are points! The game starts and stops!&lt;/p&gt;

&lt;figure style="max-width: 555px; display: flex; flex-direction: column"&gt;

  &lt;img src="https://nickivance.com/writing/20170719_smash-banana-part-two/170717_road_game_bananas_short.gif" alt="" style="width: auto"&gt;

  &lt;figcaption&gt;The car is now a proper maroon Volvo 240 station wagon and the obstacles are bananas!&lt;/figcaption&gt;

&lt;/figure&gt;

&lt;p&gt;You can play it &lt;a href="https://edamamegreen.github.io/roadgame/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The bananas were a bit of a challenge. What is a fun thing to run a car over? I felt bad about running over cute things, cows, people, aliens. Other cars would require some advanced damage animation. Stretch goal. The bananas are actually a throwback to an old DOS game I used to play with my dad - you would give an angle and a speed for a king kong sized gorilla standing on a city scape to throw an exploding banana at another gorilla.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20170719_smash-banana-part-two/gorilla_game_DOS.jpg" alt=""&gt;

&lt;/figure&gt;

&lt;p&gt;As I’ve been building, I’ve realized that there are probably many different ways to structure a game. Which functions are looping to provide continuous action? How do I bring in the input from the players and collisions? I’ve started restructuring my game but it’s about time I look at other games to see what advantages or disadvantages they have in their code structure.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20170717_stools-part-one/</id>
    <title>Sitting Stools - Part One</title>
    <published>2017-07-17T00:00:00Z</published>
    <updated>2017-07-17T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20170717_stools-part-one/" type="text/html"/>
    <content type="html">&lt;h1 id="sitting-stools-br-part-one"&gt;Sitting Stools &lt;br&gt; Part One&lt;/h1&gt;

&lt;p&gt;My partner is interested in woodworking but seemed to be delayed getting started so I challenged him to build four sitting stools over the month of July.&lt;/p&gt;

&lt;p&gt;I choose the number four because:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You’ve got to make more than one of something to get good at making it.&lt;/li&gt;
  &lt;li&gt;A week per sitting stool seemed reasonable for planning and execution.&lt;/li&gt;
  &lt;li&gt;Sitting stools can be simple to build but the possibilities are great and they don’t need to be as structurally sound as a step stool.&lt;/li&gt;
&lt;/ul&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20170717_stools-part-one/vietnam.jpg" alt=""&gt;

  &lt;figcaption&gt;The stools we saw in Vietnam really kicked off our interest. Little sidewalk restaurants use them to set out a dining area while open and stack them away after closing.&lt;/figcaption&gt;

&lt;/figure&gt;

&lt;p&gt;I’m making some sitting stools, too. My interest is stools that can be stacked easily and I found many great ideas on Pinterest. I was most impressed with a design that stacked horizontally instead of vertically.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20170717_stools-part-one/aa-stools.jpeg" alt=""&gt;

  &lt;figcaption&gt;AA Stools by Torafu Architects&lt;/figcaption&gt;

&lt;/figure&gt;

&lt;p&gt;I read about them a bit in this &lt;a href="https://www.dezeen.com/2012/12/16/aa-stools-by-torafu-architects/"&gt;Dezeen article&lt;/a&gt;. The website for Torafu Architects is &lt;a href="http://torafu.com/"&gt;www.torafu.com&lt;/a&gt;. There is a nice story about how the architects designed this as a joint project with a community workshop established after the tsunami.&lt;/p&gt;

&lt;p&gt;These seemed simple enough for me to make. My partner created some drawings for them, making up an angle for the legs.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20170717_stools-part-one/my-stools.jpg" alt=""&gt;

  &lt;figcaption&gt;These came pretty close! A bit of a mishap in the heights.&lt;/figcaption&gt;

&lt;/figure&gt;

&lt;p&gt;Before I go further, I want to say that TOOL LIBRARIES ARE AWESOME! We were able to borrow this great miter saw in the photo and it was so helpful.&lt;/p&gt;

&lt;p&gt;Making thing is hard! The photo hides a lot:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;gaps between the wood because I didn’t clamp them together while screwing in the screws&lt;/li&gt;
  &lt;li&gt;accidentally screwing in on the outside instead of the inside of the legs&lt;/li&gt;
  &lt;li&gt;an extra screw on the top to try and reduce wiggle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I took one apart and pretty much redid the whole thing. I made 3 of these so that knocked out most of the month’s requirement. ;)&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20170519_smash-banana-part-one/</id>
    <title>Smash Banana - Part 1</title>
    <published>2017-05-19T00:00:00Z</published>
    <updated>2017-05-19T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20170519_smash-banana-part-one/" type="text/html"/>
    <content type="html">&lt;h1 id="smash-banana-br-part-1"&gt;Smash Banana &lt;br&gt; Part 1&lt;/h1&gt;

&lt;p&gt;Following up on my experiment to code a moving road (as you would see it from a helicopter), I began building a driving game in Javascript. It will feature my Volvo once I get around to designing the graphics.&lt;/p&gt;

&lt;p&gt;I switched from Processing to Javascript because HTML/CSS/JS are the real workhorses underneath the web and app tools that I design in my work and I wanted to better understand the limitations and possibilities of these languages.&lt;/p&gt;

&lt;figure&gt;

  &lt;img src="https://nickivance.com/writing/20170519_smash-banana-part-one/road_game_moving-car_170506.gif" alt=""&gt;

  &lt;figcaption&gt;The road moves. And I can move the car with left/right arrow. Woo!&lt;/figcaption&gt;

&lt;/figure&gt;

&lt;figure style="max-width: 555px; display: flex; flex-direction: column"&gt;

  &lt;img src="https://nickivance.com/writing/20170519_smash-banana-part-one/road_game_two.gif" alt="" style="width: auto"&gt;

  &lt;figcaption&gt;Now I have obstacles appearing on the road. But I need them to look like they're sitting on the road, not falling down the screen.&lt;/figcaption&gt;

&lt;/figure&gt;

&lt;figure style="max-width: 555px; display: flex; flex-direction: column"&gt;

  &lt;img src="https://nickivance.com/writing/20170519_smash-banana-part-one/road_game_three.gif" alt="" style="width: auto"&gt;

  &lt;figcaption&gt;The next big task was collision detection. To start, the road obstacles just disappear when they collide with the car (the white box).&lt;/figcaption&gt;

&lt;/figure&gt;

&lt;p&gt;It’s been a lot of fun. I have a long way to go. All of this is vanilla Javascript, that means no libraries. A library might already have something like collision detection where you give a function to objects and the function tells you if they are overlapping. I wanted to figure it out for myself. Did a little trigonometry – my favorite math in high school.&lt;/p&gt;

&lt;p&gt;Next, I want the obstacles to bounce away from the car.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20170206_moving-road/</id>
    <title>Make a Moving Road</title>
    <published>2017-02-06T00:00:00Z</published>
    <updated>2017-02-06T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20170206_moving-road/" type="text/html"/>
    <content type="html">&lt;h1 id="make-a-moving-road"&gt;Make a Moving Road&lt;/h1&gt;

&lt;p&gt;This past week, one of my experiments has been to program a moving road. I’d love to make a driving game, a super basic 2D game where your car moves only left and right and the road appears to move under you. I set myself the task of making just the appearance of a moving road. I used Processing (a programming language) because it’s familiar; I spent a semester playing with in graduate school.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20170206_moving-road/road.png" alt=""&gt;

  &lt;figcaption&gt;The white box is going to be my car. Right now it follows my mouse.&lt;/figcaption&gt;

&lt;/figure&gt;

&lt;p&gt;Basically, my road is a set of dashes that move down the screen each time a new frame is drawn. One dash was easy. Then I created a set of dashes to fill the screen but the set would just move down the screen, leaving it empty above the first dash. I was resetting the position of the set based on the the first dash reaching the bottom of the screen, so the rest were further below the screen edge. Now I have a set of dashes twice as tall as the screen so visually cycles. And I made it to fill whatever height screen you have. Extra credit. But this method isn’t efficient because I have twice as many dashes as I need. My next experiment could be to find a way to drop the dashes onto the screen individually, starting each one over as it reaches the bottom.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20170123_unocha-launch/</id>
    <title>Launched: New site for UNOCHA's Financial Tracking Service</title>
    <published>2017-01-23T00:00:00Z</published>
    <updated>2017-01-23T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20170123_unocha-launch/" type="text/html"/>
    <content type="html">&lt;h1 id="launched-new-site-for-unochas-financial-tracking-service"&gt;Launched: New site for UNOCHA’s Financial Tracking Service&lt;/h1&gt;

&lt;p&gt;Congratulations! After over a year of hard work, the Financial Tracking Service team just launched their improved website for sharing data on humanitarian aid funding.&lt;/p&gt;

&lt;p&gt;I’ve enjoyed working with these folks since August. My focus has been on the Data Search feature that makes it possible for the humanitarian community to download and make use of the data FTS collects.&lt;/p&gt;

&lt;p&gt;Check out the site: &lt;a href="https://fts.unocha.org/"&gt;https://fts.unocha.org/&lt;/a&gt;&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20170123_unocha-launch/FTS_launch_012317_launch.png" alt=""&gt;

&lt;/figure&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20170118_yelle/</id>
    <title>Be Excited: Ici &amp; Maintenant</title>
    <published>2017-01-18T00:00:00Z</published>
    <updated>2017-01-18T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20170118_yelle/" type="text/html"/>
    <content type="html">&lt;h1 id="be-excited-ici--maintenant"&gt;Be Excited: Ici &amp;amp; Maintenant&lt;/h1&gt;

&lt;p&gt;Excitement category: music videos&lt;/p&gt;

&lt;p&gt;I don’t know about you but there are some music/dance videos that inspire such embarrassing feelings of happiness and love that I question my agnosticism. When I am feeling bummed, watching one of my favorite music videos will turn me around. And possibly waste an hour on YouTube.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=DMeyvg1M52k"&gt;Ici &amp;amp; Maintenant by Yelle on YouTube&lt;/a&gt;&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20170118_yelle/yelle.png" alt=""&gt;

&lt;/figure&gt;

&lt;p&gt;My latest favorite is Yelle’s Ici &amp;amp; Maintenant (Here &amp;amp; Now) and it’s destined to stay at the top of my list for a long time. The video fulfills my basic criteria for a good video:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;it tells a fun story,&lt;/li&gt;
  &lt;li&gt;it makes the most of mood in the music,&lt;/li&gt;
  &lt;li&gt;the dancing makes me want to dance, too, and&lt;/li&gt;
  &lt;li&gt;it doesn’t have characters who are depressed, cruel, or only sexual.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What takes this video to awesome-as-space level is that it is playful, inclusive art. The team that created this video took an odd idea, a relationship in which one person is always moving spastically, and they delved into the absurdity. Who are these people? Why is one always moving? Are they in a relationship? What kind of relationship?&lt;/p&gt;

&lt;p&gt;As I watch, I go from being curious and amused to becoming attached to this endearing couple and the mystery of their lives. And at the end, I’m rewarded with a fun surprise - a final clue that extends the mystery. Did they switch places at midnight? What does that mean?&lt;/p&gt;

&lt;p&gt;I don’t know French so perhaps understanding the lyrics might change my opinion. The Google translate version of the lyrics doesn’t seem unexpected or inappropriate so I’ll stick to my opinion: I absolutely love it and I’ll probably be happier and more creative if I watch it every morning.&lt;/p&gt;

&lt;p&gt;Hope you like it, too!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20160720_papelpicado/</id>
    <title>Papel Picado</title>
    <published>2016-07-20T00:00:00Z</published>
    <updated>2016-07-20T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20160720_papelpicado/" type="text/html"/>
    <content type="html">&lt;h1 id="papel-picado"&gt;Papel Picado&lt;/h1&gt;

&lt;p&gt;My friend, Sabrina, asked me to make a sign for her wedding. Something to put out in front of the house where the wedding would be hosted, to help people find the place. The wedding had a Mexican theme with bright paper flowers and paper cut flags (papel picado).&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20160720_papelpicado/picado-one.jpeg" alt=""&gt;

&lt;/figure&gt;

&lt;p&gt;I spent a while looking at papel picado examples and came up with a design. I printed a black &amp;amp; white version of the design and gathered some plywood and hinges. I was able to get the plywood store to cut a sheet down into the right sizes to save myself a step (also I’m still learning how to cut straight with a skilsaw). I tried a few methods for transferring my design onto the wood and found the best method on the third try. I covered the plywood with masking tape and carved over the paper design through the tape. Then I could remove the tape from the areas I wanted to paint. This made painting super easy.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20160720_papelpicado/picado-two.jpeg" alt=""&gt;

&lt;/figure&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20160720_papelpicado/picado-three.jpeg" alt=""&gt;

&lt;/figure&gt;

&lt;p&gt;When it was dry, I pulled up the remaining tape and it left crisp lines. Let me tell you, pulling up the tape after painting was soooo satisfying. Assembling the sign was pretty simple after that.&lt;/p&gt;

&lt;p&gt;Congratulations, friends!&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20160720_papelpicado/picado-four.gif" alt=""&gt;

&lt;/figure&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20160607_webvisions/</id>
    <title>PDX Webvisions Conference 2016: My favorite talks from bearded men</title>
    <published>2016-06-07T00:00:00Z</published>
    <updated>2016-06-07T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20160607_webvisions/" type="text/html"/>
    <content type="html">&lt;h1 id="pdx-webvisions-conference-2016-my-favorite-talks-from-bearded-men"&gt;PDX Webvisions Conference 2016: My favorite talks from bearded men&lt;/h1&gt;

&lt;p&gt;I don’t know how many bearded men spoke at Webvisions in Portland this year but I went specifically to see Aaron Draplin and Mike Monteiro. Both were epic and inspiring and qualify for the “watch again” category.&lt;/p&gt;

&lt;p&gt;Aaron Draplin is the pride of the Portland design scene because he is a prolific, well-known designer of logos, posters, and the creator of much-beloved Field Notes. I admit I thought maybe Portland overhyped him because he’s a hometown hero - but no, he’s explicatively fantastic. He gave a standard here’s-how-I-became-a-designer talk but it was awesome because it came with so much happiness, authentic gratitude, and love for the world. This guy loves the doing the design he does and he told a touching story about how he was able to help a guy out a tough spot with a good logo. I think he’s such a happy guy because he is doing what he loves doing and doesn’t take it for granted.&lt;/p&gt;

&lt;p&gt;I feel lucky that I feel the same way. I’m going to be able to look back on my career and how it impacted my life in the same way. I feel like a rockstar because I get to do UX design as a job. I’ve felt that way since I got my first job, barely knew nothing, barely paid much, but totally in love with UX.&lt;/p&gt;

&lt;p&gt;Mike Monteiro is a master at giving talks. His talks aren’t just good talks, they’re next level. He hits you in the gut with meaning and spins a bunch of tops that wobble around in your brain for days. His talk this year addressed a problem I didn’t even know existed. There’s a huge demand for designers and there aren’t enough good designers prepared for it. Sounds like a good problem to have. Except that our lives are spent interacting with designed products and services and we suffer when they aren’t designed well.&lt;/p&gt;

&lt;p&gt;Mike covered how schools and industry aren’t doing enough to educate new designers. I paid a lot of attention here because, as a UX manager, I wondered a lot about whether the lack of satisfactory design candidates was a problem we should be solving by better educating new hires. The solution, Mike encouraged, was to give back by taking on this education, either formally with hires or informally through mentoring. I especially liked that he modeled a community attitude by explaining that when his teammates are ready to move on that he helps them find their next job. Even though it’s a loss to have someone outgrow your team, you’ve made a contribution. I’m very thankful that I had a couple good mentors and managers in particular that helped me learn in leaps and bounds.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20160515_cabinet/</id>
    <title>A cabinet is a portal to the future - when you're still building it</title>
    <published>2016-05-15T00:00:00Z</published>
    <updated>2016-05-15T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20160515_cabinet/" type="text/html"/>
    <content type="html">&lt;h1 id="a-cabinet-is-a-portal-to-the-future---when-youre-still-building-it"&gt;A cabinet is a portal to the future - when you’re still building it&lt;/h1&gt;

&lt;p&gt;2 weeks ago, Kevin and I started working on building a cabinet. We wanted it to fit in a small space between the kitchen door and a heater. I’m working on making the most of the space I have and reducing the things I keep in it.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20160515_cabinet/cabinet.png" alt=""&gt;

&lt;/figure&gt;

&lt;p&gt;The cabinet is 2 feet wide and 6 feet tall. We made it from laminated strand lumber (LSL) studs (from Canada!). This is an odd choice because this means the walls of the cabinet are 1.75 inches thick, about an inch thicker than most cabinets. Why we chose them is a story for another day. They do look cool. The LSL is a bunch of lumber shredded and glued back together so it has a crazy pattern.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20160515_cabinet/sidegrain.jpeg" alt=""&gt;

&lt;/figure&gt;

&lt;p&gt;We went through some of the normal steps of cabinet-making, perhaps slowly and out of order. Drew it all out, cut it to size. Bought some medium-density fibreboard (MDF) for backing and plywood for the doors. We chose knock-down hardware (think Ikea-style) because it will be big (and heavy!) enough that we’ll probably want to take it apart to move it. Sanded the exterior and coated it with polyurethane twice.&lt;/p&gt;

&lt;p&gt;The LSL didn’t look so great on the flat board side. We just liked the grain on the edges. To improve the look of it, we milled the visible side with a triangle pattern. The pattern cuts in up to .5 inches so the grain begins to show.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20160515_cabinet/cnc.jpeg" alt=""&gt;

&lt;/figure&gt;

&lt;p&gt;It’s harder to see the pattern after we sanded it. I kinda miss the drill marks.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20160515_cabinet/side.jpeg" alt=""&gt;

&lt;/figure&gt;

&lt;p&gt;Here it is. Stage 1 complete. The next stage is to start putting stuff on it and thinking about what needs to go in there. Then we’ll place the rest of the shelves in optimal levels. I’ll also do the doors.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20160515_cabinet/cabinet-full.jpeg" alt=""&gt;

&lt;/figure&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20160511_archery-bag/</id>
    <title>Archery bag complete</title>
    <published>2016-05-11T00:00:00Z</published>
    <updated>2016-05-11T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20160511_archery-bag/" type="text/html"/>
    <content type="html">&lt;h1 id="archery-bag-complete"&gt;Archery bag complete&lt;/h1&gt;

&lt;p&gt;Update on &lt;a href="http://www.nickivance.com/writing/20160425_sewing/index.html"&gt;Trying a New Medium: Sewing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The bag is finished! I was lagging on sewing up the bottom.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20160511_archery-bag/archery.jpeg" alt=""&gt;

&lt;/figure&gt;

&lt;p&gt;I tried a different approach to sewing the bottom. I sewed in a straight line and then folded it flat. Then I sewed across the triangle corners that are created when it is flat.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20160511_archery-bag/progress-one.jpeg" alt=""&gt;

&lt;/figure&gt;

&lt;p&gt;Yeah, the tension was all out of whack. I’ll probably have to re-do the bottom soon.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20160511_archery-bag/progress-two.jpeg" alt=""&gt;

&lt;/figure&gt;

&lt;p&gt;The bottom ends up looking like this. Like a very wide “H”. This was much easier than the first way I tried. Below is my first try. I sewed darts in 4 corners and then across the middle. The bottom comes out with a shape like “&amp;gt;–&amp;lt;”. I think it looks a little nicer so maybe I’ll use it again on another project.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20160511_archery-bag/progress-three.jpeg" alt=""&gt;

&lt;/figure&gt;

&lt;h2 id="faqs"&gt;FAQs&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What are those spots on the side?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’m not sure, maybe oil stains from stickers. The fabric came like this and was super discounted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What kind of fabric is this?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s urethane coated ripstop nylon. The awesome folks where I got it said that this is the material Arc’teryx uses on their bags. It does look like a match for a bag Arc’teryx used to sell but is not longer on their website. They describe the material as:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Used on Arc'teryx alpine climbing packs, the NC400r-AC² nylon 6 ripstop material is ultra light, but very strong and durable. Urethane coated on both sides, the fabric is waterproof and air-impermeable, and has a white coated interior for increased visibility inside the bag.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I can tell you it’s great because it has a grid in it. This made cutting straight lines easy. It’s super difficult for actually sewing because its coated surface makes it hard for the sewing machine foot to pull it through smoothly.&lt;/p&gt;

&lt;h2 id="update"&gt;Update:&lt;/h2&gt;
&lt;p&gt;I’ve taken the bag to the range a few times and it’s holding up great!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20160425_sewing/</id>
    <title>Trying a new medium: Sewing</title>
    <published>2016-04-25T00:00:00Z</published>
    <updated>2016-04-25T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20160425_sewing/" type="text/html"/>
    <content type="html">&lt;h1 id="trying-a-new-medium-sewing"&gt;Trying a new medium: Sewing&lt;/h1&gt;

&lt;p&gt;One of my goals for my time off was to try out sewing. I’d spent a lot of time investigating the perfect backpack a couple years ago and while in Japan in January, really admired a lot of the backpacks and men’s clutches that I saw. I decided to try it out. This was an easy decision because my SO was interested in the same thing and had already gotten a sewing machine set up in my apartment.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20160425_sewing/sewing.png" alt="Sewing supplies"&gt;
&lt;/figure&gt;

&lt;h2 id="first-project"&gt;First Project&lt;/h2&gt;

&lt;p&gt;My first project was to tear apart a pencil case he’d made in an introductory case and sew it back together. It was a good way to get familiar with the sewing machine. Sewing straight lines, thinking about the order of operations, and doing the reverse stitch to end each section.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20160425_sewing/bag.jpeg" alt="Pencil case bag"&gt;
&lt;/figure&gt;

&lt;h2 id="second-project-archery-bag"&gt;Second project: Archery bag&lt;/h2&gt;

&lt;p&gt;I’m on my second project now. An archery bag. Here are my requirements:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;it stands up on a flat bottom&lt;/li&gt;
  &lt;li&gt;it has a way to hold the quiver upright&lt;/li&gt;
  &lt;li&gt;it has pockets for various items that often get tangled in my basic tote bag&lt;/li&gt;
  &lt;li&gt;I can carry it over a single shoulder like a tote bag.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s not a backpack or anything to be used in hunting. Just to hold my stuff on the ground while I shoot.&lt;/p&gt;

&lt;h2 id="first-attempt"&gt;First attempt&lt;/h2&gt;

&lt;p&gt;I pulled a basic bag together that could hold a quiver and had a flat bottom. Making a flat bottom from a cylinder was the most difficult and most fun part of this project. I had looked at a few other bags and borrowed some ideas about how to create a flat bottom.&lt;/p&gt;

&lt;p&gt;It wasn’t pretty, but it made a flat-bottomed bag! Then I started thinking about adding pockets and realized I was going to have a hard time trying to fit my sewing machine into a tote bag.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20160425_sewing/firstattempt.jpeg" alt="First attempt at the archery bag"&gt;
&lt;/figure&gt;

&lt;h2 id="second-attempt"&gt;Second attempt&lt;/h2&gt;

&lt;p&gt;I really really love pockets. Gotta have ‘em. So I deconstructed my bag and cut fabric for pockets and started all over again. This is great because it means I can do a better job the second time around. More practice!&lt;/p&gt;

&lt;p&gt;After 3 hours, I got the pockets in, handles, and made the bag’s cylinder shape.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20160425_sewing/openbag.jpeg" alt="Open bag showing interior"&gt;
&lt;/figure&gt;

&lt;figure style="max-width: 555px"&gt;
  &lt;img src="https://nickivance.com/writing/20160425_sewing/assembledpockets.jpeg" alt="Assembled pockets"&gt;
&lt;/figure&gt;

&lt;p&gt;Next time, I’ll work on the bottom again. I’m having a great time. So far I enjoy this a lot more than many of the other projects I’ve tried to do. I love the spatial problems of taking flat cloth and making 3D shapes. I also might enjoy it because I’m not worry about the details, or making sure this looks good. I’m happy to be prototyping.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20160328_leaving-citizen/</id>
    <title>Leaving Citizen</title>
    <published>2016-03-28T00:00:00Z</published>
    <updated>2016-03-28T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20160328_leaving-citizen/" type="text/html"/>
    <content type="html">&lt;h1 id="leaving-citizen"&gt;Leaving Citizen&lt;/h1&gt;

&lt;p&gt;I left Citizen, Inc. a week ago. It was incredibly touching. I’ll miss everyone there.
I decided that it was time for me to get out of my comfort zone. I want to learn how other people design digital products. And I want to know if I can find a work schedule where I have time for side projects, daily flexibility to stay healthy, and can travel frequently.
It might not be possible. I’m not sure what form my ideal job would take. But I know I need to try to find out and the exploration makes me happy.&lt;/p&gt;

&lt;p&gt;I’m going to take on some contract work. I’m taking interviews for full-time work and learning how to evaluate what a company has to offer. I might schedule a few weeks for strictly personal projects. TBD.&lt;/p&gt;

&lt;figure style="max-width: 555px"&gt;

  &lt;img src="https://nickivance.com/writing/20160328_leaving-citizen/citizen.jpeg" alt=""&gt;

&lt;/figure&gt;
</content>
  </entry>
  <entry>
    <id>https://nickivance.com/writing/20151001_xoxo/</id>
    <title>Heck Yes: XOXO 2015</title>
    <published>2015-10-01T00:00:00Z</published>
    <updated>2015-10-01T00:00:00Z</updated>
    <link rel="alternate" href="https://nickivance.com/writing/20151001_xoxo/" type="text/html"/>
    <content type="html">&lt;h1 id="heck-yes-xoxo-2015"&gt;Heck Yes: XOXO 2015&lt;/h1&gt;

&lt;h2 id="background"&gt;Background&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://xoxofest.com/"&gt;XOXO&lt;/a&gt; is a Portland festival &amp;amp; conference that has been held four times already. The event organizers (two Andys) curate a selection of talks, films, storytelling events, music, and game demos that represent the best content the web has to offer made by people who’s efforts make the world a better place. It is also a well-designed experience that ensures everyone is happy, intellectually alive, and excited to meet strangers. It is a microcosm of an ideal culture; a culture that it is positive and self-aware, and always working to improve.&lt;/p&gt;

&lt;h2 id="heck-yes"&gt;Heck Yes&lt;/h2&gt;
&lt;p&gt;I go to XOXO because I can see real life examples of the kind of success I seek. I love that the people I see there are thoughtful and strong. Their projects offer more diverse content and help our culture become healthier.&lt;/p&gt;

&lt;p&gt;My favorite parts are the talks and the video game arcade. I love the art and interaction in games. I design user interfaces so games represent the opportunity to design for enjoyment instead of task completion.&lt;/p&gt;

&lt;p&gt;The talks blew me away because I identified with the many smart, articulate, and funny women who took the stage and shared their ideas of what it takes to make our lives better.&lt;/p&gt;
</content>
  </entry>
</feed>

