<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[If We Go]]></title><description><![CDATA[Projects that never get finished.]]></description><link>https://ifwego.co/</link><image><url>https://ifwego.co/favicon.png</url><title>If We Go</title><link>https://ifwego.co/</link></image><generator>Ghost 5.79</generator><lastBuildDate>Tue, 21 Apr 2026 11:35:23 GMT</lastBuildDate><atom:link href="https://ifwego.co/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Three Swings at Growing Coffee]]></title><description><![CDATA[<p>I&apos;ve tried twice to grow coffee from seed at home and bought coffee plants on a few occasions. In this post, I want to document my attempts and what I&apos;ve learned both for my own notes and in case other people want to give it a</p>]]></description><link>https://ifwego.co/growing-coffee/</link><guid isPermaLink="false">6580d0497f8c5000015f0791</guid><category><![CDATA[Coffee]]></category><dc:creator><![CDATA[Daniel Pok]]></dc:creator><pubDate>Fri, 17 May 2024 21:47:21 GMT</pubDate><media:content url="https://ifwego.co/content/images/2024/05/IMG_1407-2.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://ifwego.co/content/images/2024/05/IMG_1407-2.jpg" alt="Three Swings at Growing Coffee"><p>I&apos;ve tried twice to grow coffee from seed at home and bought coffee plants on a few occasions. In this post, I want to document my attempts and what I&apos;ve learned both for my own notes and in case other people want to give it a try.</p><p>If you haven&apos;t had a good look at a coffee tree before, here are some pictures I took at Kew Gardens in London. The tree they had in the greenhouse was <em>coffea canephora</em> (robusta), but they&apos;re also <a href="https://www.kew.org/about-us/press-media/forgotten-coffee-species-futureproofing-industry-against-climate-change?ref=ifwego.co" rel="noreferrer">doing research on other species of coffee</a> that may help protect coffee against threats from climate change.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/IMG_1393.jpg" width="1015" height="1353" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_1393.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_1393.jpg 1000w, https://ifwego.co/content/images/2024/05/IMG_1393.jpg 1015w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/IMG_1402.jpg" width="1804" height="1353" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_1402.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_1402.jpg 1000w, https://ifwego.co/content/images/size/w1600/2024/05/IMG_1402.jpg 1600w, https://ifwego.co/content/images/2024/05/IMG_1402.jpg 1804w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/IMG_1412.jpg" width="1804" height="1353" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_1412.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_1412.jpg 1000w, https://ifwego.co/content/images/size/w1600/2024/05/IMG_1412.jpg 1600w, https://ifwego.co/content/images/2024/05/IMG_1412.jpg 1804w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Kew Gardens is doing research on a species named </span><i><em class="italic" style="white-space: pre-wrap;">coffea</em></i> <i><em class="italic" style="white-space: pre-wrap;">stenophylla</em></i><span style="white-space: pre-wrap;"> that they hope will make coffee production more robust in the face of climate change.</span></p></figcaption></figure><p> On the surface, growing coffee sounds pretty doable: the coffee we drink is the roasted seed of the coffee plant after all, and Trader Joe&apos;s sold coffee plants as indoor house plants that one time. Since I have roaster friends around the Bay Area, it wasn&apos;t too hard for me to get access to coffee in different stages of processing to play with.</p><h2 id="my-first-try-starting-with-cherry">My First Try: Starting with Cherry</h2><p>When I was <a href="https://ifwego.co/tag/coffee-june/" rel="noreferrer">taking classes at Boot Coffee Campus</a>, they had a pair of coffee trees growing in a raised planter by the door. That tree had fresh fruit and flowers, and it was the first time I&apos;d tasted coffee fruit.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/IMG_0432.jpg" width="1015" height="1353" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_0432.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_0432.jpg 1000w, https://ifwego.co/content/images/2024/05/IMG_0432.jpg 1015w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/IMG_0153.jpg" width="1015" height="1353" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_0153.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_0153.jpg 1000w, https://ifwego.co/content/images/2024/05/IMG_0153.jpg 1015w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/IMG_0152--1-.jpg" width="1015" height="1353" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_0152--1-.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_0152--1-.jpg 1000w, https://ifwego.co/content/images/2024/05/IMG_0152--1-.jpg 1015w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/IMG_0157--1-.jpg" width="1015" height="1353" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_0157--1-.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_0157--1-.jpg 1000w, https://ifwego.co/content/images/2024/05/IMG_0157--1-.jpg 1015w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Various angles of the two coffee trees in the planter at Boot Coffee Campus, featuring my friend Ming from </span><a href="https://www.instagram.com/moonwakecoffeeroasters/?ref=ifwego.co" rel="noreferrer"><span style="white-space: pre-wrap;">@moonwakecoffeeroasters</span></a><span style="white-space: pre-wrap;"> for scale. The two trees are close together and their branches interleave, sot it&apos;s hard to see that there are two.</span></p></figcaption></figure><p>These trees are really beautiful in person. They have long, slender branches and the leaves hang off them in orderly rows like strung-up lanterns or dangling paper kites. The red fruits sit tantalizingly at every node, and the flowers held aloft by the branches catch every passing breeze and fill the area with a fragrance like strong jasmine and faint perfume.</p><p>Because I started from fresh fruit, the seed was still wrapped in a protective layer of parchment. This sturdy layer is removed during coffee processing, leaving the green coffee seed. I tried germinating the coffee in the parchment at first, and while it was slow, several of the seeds did manage to germinate that way. After a while, I got impatient and started soaking them in water. This greatly sped up the germination process and softened the parchment, allowing me to remove it manually.</p><p>I germinated them on the surface of the soil, which was tedious because I had to mist them every few days for weeks on end until they put roots into the soil. I considered just sandwiching them between some moist towels, but I feared that this would cause mold. It would also have been easier if I buried them in the soil and covered the seeding tray with a greenhouse cover (e.g. clear plastic lid) since the soil would keep them evenly moist without having to water them as often, however the downside is that you can&#x2019;t see if they&#x2019;ve germinated or not. Since it takes almost two months to germinate, it&#x2019;s a long wait and I wanted to check that my seedlings were alive. For a grower with a lot of parchment coffee or whole cherry, a &#x201C;spray-and-pray&#x201D; approach where you bury 3 cherries in each nursery pot and then thin them down to one per pot after they germinate might be more efficient and reliable, but when you only have a handful of cherries and want to maximize the number of viable seedlings, you kinda want to baby them (perhaps more than is good for them).</p><p>Here are some photos of the germination process starting from the seeds that I extracted from the coffee fruit to the point where the seedlings unfurled their cotyledons (seed leaves).</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/IMG_0118.jpg" width="1015" height="1353" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_0118.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_0118.jpg 1000w, https://ifwego.co/content/images/2024/05/IMG_0118.jpg 1015w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/image-4.jpeg" width="2000" height="2667" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/image-4.jpeg 600w, https://ifwego.co/content/images/size/w1000/2024/05/image-4.jpeg 1000w, https://ifwego.co/content/images/size/w1600/2024/05/image-4.jpeg 1600w, https://ifwego.co/content/images/2024/05/image-4.jpeg 2000w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/image-2.jpeg" width="2000" height="2667" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/image-2.jpeg 600w, https://ifwego.co/content/images/size/w1000/2024/05/image-2.jpeg 1000w, https://ifwego.co/content/images/size/w1600/2024/05/image-2.jpeg 1600w, https://ifwego.co/content/images/2024/05/image-2.jpeg 2000w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/image-3.jpeg" width="2000" height="2667" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/image-3.jpeg 600w, https://ifwego.co/content/images/size/w1000/2024/05/image-3.jpeg 1000w, https://ifwego.co/content/images/size/w1600/2024/05/image-3.jpeg 1600w, https://ifwego.co/content/images/2024/05/image-3.jpeg 2000w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/image-1.jpeg" width="2000" height="2667" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/image-1.jpeg 600w, https://ifwego.co/content/images/size/w1000/2024/05/image-1.jpeg 1000w, https://ifwego.co/content/images/size/w1600/2024/05/image-1.jpeg 1600w, https://ifwego.co/content/images/2024/05/image-1.jpeg 2000w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/IMG_3016.jpg" width="1015" height="1353" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_3016.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_3016.jpg 1000w, https://ifwego.co/content/images/2024/05/IMG_3016.jpg 1015w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/IMG_2977.jpeg" width="1200" height="900" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_2977.jpeg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_2977.jpeg 1000w, https://ifwego.co/content/images/2024/05/IMG_2977.jpeg 1200w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Here are photos showing coffee germinating from the seed (still encased in its parchment layer) to when they unfurled their cotyledons (seed leaves). From first to last: seeds in parchment, germinating in the seeding tray, two shots of the seedlings at the &quot;solider&quot; (bean on a pole) stage, cotyledons pushing out of the coffee bean, furled cotyledons after freeing themselves, and finally unfurled cotyledons (if you look closely, you can see a pair of tiny leaves in the center). </span></p></figcaption></figure><p>My attrition rate was really high. I started with about 12-15 seeds and ended up with two surviving plants. If I recall correctly, about 10 germinated, 6-8 made it to the soldier stage, and then 4 survived to put out true leaves. I think I could&apos;ve improved my germination process, which would&apos;ve given the plants more energy to grow through their early seedling stages.</p><p>Here they are putting out their first true leaves:</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/IMG_3111.jpg" width="2000" height="2000" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_3111.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_3111.jpg 1000w, https://ifwego.co/content/images/size/w1600/2024/05/IMG_3111.jpg 1600w, https://ifwego.co/content/images/size/w2400/2024/05/IMG_3111.jpg 2400w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/IMG_3029.jpg" width="1015" height="1353" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_3029.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_3029.jpg 1000w, https://ifwego.co/content/images/2024/05/IMG_3029.jpg 1015w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Two pictures of the seedlings showing their cotyledons (round seed leaves) and true leaves (pointy). If you look closely, you can see the tiny, pyramid shaped growth bud at the tip of the shoot.</span></p></figcaption></figure><p>Here&apos;s what they look like after a year of growth. You can see the round cotyledons as the lowest pair of leaves, but the plant has since grown several pairs of true leaves (which are pointed).</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/05/IMG_3728.jpg" class="kg-image" alt="Three Swings at Growing Coffee" loading="lazy" width="2000" height="1379" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_3728.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_3728.jpg 1000w, https://ifwego.co/content/images/size/w1600/2024/05/IMG_3728.jpg 1600w, https://ifwego.co/content/images/2024/05/IMG_3728.jpg 2000w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">The two surviving coffee seedlings, chilling on my kitchen windowsill. I put them here once they got bigger so that they&apos;d get more sunlight.</span></figcaption></figure><p>I suspect they&apos;re growing slower than they should, but my house is neither an Ethiopian cloud forest nor the Panamanian mountains, so I&apos;ll let them take their time.</p><h2 id="my-second-try-buying-a-plant">My Second Try: Buying A Plant</h2><p>I got impatient and figured I&apos;d hedge my bets, so when I saw that FRINJ were selling coffee trees at SF Coffee Fest, I bought one. FRINJ is a California based coffee grower and one of the only producers in the continental United States. If anyone had trees that were well-adapted to the California climate, I figured it&apos;d be them.</p><p>Some sources on the internet say that coffee plants take a few months to get to be a few inches tall, then within the first year, they should be around 2 ft. This lines up with the tree I got at Coffee Fest. I think it&#x2019;s about a year old. All told, buying trees from FRINJ for $35 seemed like a good deal given how many hours of babying it took for me to get two viable plants from seed.</p><p> I had to carry the tree in my backpack across San Francisco and take it home on public transit, which was fun.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2023/12/image.jpeg" class="kg-image" alt="Three Swings at Growing Coffee" loading="lazy" width="2000" height="2667" srcset="https://ifwego.co/content/images/size/w600/2023/12/image.jpeg 600w, https://ifwego.co/content/images/size/w1000/2023/12/image.jpeg 1000w, https://ifwego.co/content/images/size/w1600/2023/12/image.jpeg 1600w, https://ifwego.co/content/images/2023/12/image.jpeg 2000w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">On a MUNI streetcar with a bunch of tourists lugging their bags over from Fishermans&#x2019; Wharf to the Embarcadero.</span></figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2023/12/IMG_2688.jpeg" class="kg-image" alt="Three Swings at Growing Coffee" loading="lazy" width="2000" height="2667" srcset="https://ifwego.co/content/images/size/w600/2023/12/IMG_2688.jpeg 600w, https://ifwego.co/content/images/size/w1000/2023/12/IMG_2688.jpeg 1000w, https://ifwego.co/content/images/size/w1600/2023/12/IMG_2688.jpeg 1600w, https://ifwego.co/content/images/2023/12/IMG_2688.jpeg 2000w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">And then waiting for BART</span></figcaption></figure><p>If you want a coffee plant of a variety actually known to produce good quality coffee, you can order one from them online &#x2013; you don&apos;t need to grab it in person like I did. Unless you have a full-scale farming operation, it&apos;s unlikely that you&apos;d grow enough coffee at home to process, roast, and brew for yourself, but I&apos;m still debating growing a small coffee garden to have some demo plants to use as educational aids.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://frinjcoffee.com/product/frinj-geisha-coffee-trees/?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">FRINJ Geisha Coffee Trees - FRINJ Coffee</div><div class="kg-bookmark-description">COFFEE PLANT CARE INSTRUCTIONS This coffee plant originated from plants grown organically in the hills of Santa Barbara, the home of California&#x2019;s first commercial coffee farm. With just a little sun, moist soil and occasional feeding this tree will make [&#x2026;]</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://frinjcoffee.com/wp-content/uploads/2023/02/favicon-152.png" alt="Three Swings at Growing Coffee"><span class="kg-bookmark-author">FRINJ Coffee</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://frinjcoffee.com/wp-content/uploads/2022/08/59A57848-EBC6-4C7D-9C69-361E6C6B0649-scaled.jpeg" alt="Three Swings at Growing Coffee"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://frinjcoffee.com/product/frinj-coffee-trees/?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">FRINJ Catuai Rojo Coffee Trees - FRINJ Coffee</div><div class="kg-bookmark-description">COFFEE PLANT CARE INSTRUCTIONS This coffee plant originated from plants grown organically in the hills of Santa Barbara, the home of California&#x2019;s first commercial coffee farm. With just a little sun, moist soil and occasional feeding this tree will make [&#x2026;]</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://frinjcoffee.com/wp-content/uploads/2023/02/favicon-152.png" alt="Three Swings at Growing Coffee"><span class="kg-bookmark-author">FRINJ Coffee</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://frinjcoffee.com/wp-content/uploads/2022/08/59A57848-EBC6-4C7D-9C69-361E6C6B0649-scaled.jpeg" alt="Three Swings at Growing Coffee"></div></a></figure><p>After a few months, my tree is still doing well and has put out new leaves. I up-potted it into a tree-sized nursery pot and it seems happy there.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/05/IMG_3031-1.jpg" class="kg-image" alt="Three Swings at Growing Coffee" loading="lazy" width="1006" height="1057" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_3031-1.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_3031-1.jpg 1000w, https://ifwego.co/content/images/2024/05/IMG_3031-1.jpg 1006w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">You can see the growth buds at the tips of each branch.</span></figcaption></figure><p>I put the planter on a rolling tray and wheeled it out for various coffee events. I find it easier to explain the process of creating coffee to people when they can see it as a plant instead of thinking about it as a bagged product.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/05/IMG_2913.jpg" class="kg-image" alt="Three Swings at Growing Coffee" loading="lazy" width="2000" height="2000" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_2913.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_2913.jpg 1000w, https://ifwego.co/content/images/size/w1600/2024/05/IMG_2913.jpg 1600w, https://ifwego.co/content/images/2024/05/IMG_2913.jpg 2000w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">It&apos;s grown a bit since I took this photo, so I&apos;m not sure if I can fit it in my backseat anymore.</span></figcaption></figure><h2 id="varieties">Varieties</h2><p>I&#x2019;m unsure what any of the coffee plants I have are. I think the FRINJ one is Red Catuai because it has a red tag on it. The seedlings came from the two trees from Boot, but I&#x2019;m unsure which they are: one tree was a gesha, the other is unknown. Maybe when they grow up I can try to identify them.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://perfectdailygrind.com/2019/06/geisha-bourbon-more-how-to-identify-6-coffee-varieties/?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Geisha, Bourbon, &amp; More: How to Identify 6 Coffee Varieties</div><div class="kg-bookmark-description">Do you know what Geisha/Gesha looks like? How about Bourbon? Read on to learn more about identifying coffee varieties.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://perfectdailygrind.com/wp-content/uploads/2020/02/cropped-pdg-icon-270x270.png" alt="Three Swings at Growing Coffee"><span class="kg-bookmark-author">Perfect Daily Grind</span><span class="kg-bookmark-publisher">Sawang Thongdee</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://perfectdailygrind.com/wp-content/uploads/2019/06/20190424_130644-scaled.jpg" alt="Three Swings at Growing Coffee"></div></a></figure><h2 id="my-third-try-germinating-green-coffee">My Third Try: Germinating Green Coffee</h2><p>Again, green coffee is just the coffee seed, removed from its outer layers. Some aggressive processing methods might chemically affect the seed, but processes like washed and natural coffees aren&apos;t too different than the processes that a coffee seed might encounter in the wild. I figured it was worth trying to get some green coffee to germinate. How cool would it be to be able to serve someone an excellent pour over and then show them a small plant germinated from the same coffee seeds?</p><p>Tom from Sweet Maria&#x2019;s says <a href="https://library.sweetmarias.com/growing-coffea-arabica-at-home/?ref=ifwego.co" rel="noreferrer">he&#x2019;s tried growing coffee from green</a> but it doesn&#x2019;t work for him (25-30% germinated, and about 1 in 50 germinated seeds actually grew). I soaked about 300 seeds and hoped to get at least one live start.</p><p>I read up on several guides on how to germinate green coffee, and settled on just soaking them until they put out embryos.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://realgoodcoffeeco.com/blogs/realgoodblog/how-to-grow-a-coffee-plant-at-home?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">How to Grow a Coffee Plant at Home</div><div class="kg-bookmark-description">This guide will outline how to grow your own coffee plant, how to care for the plant, tips for growth, and common questions.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://realgoodcoffeeco.com/cdn/shop/files/Artboard_8_32x32.png?v=1635954270" alt="Three Swings at Growing Coffee"><span class="kg-bookmark-author">Real Good Coffee Co.</span><span class="kg-bookmark-publisher">Adam Gante</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://realgoodcoffeeco.com/cdn/shop/articles/00-cover.png?v=1572894081" alt="Three Swings at Growing Coffee"></div></a></figure><p>A good number of them actually germinated. I should&apos;ve taken more detailed notes at the time, but I think about 30% of the green coffee I soaked germinated.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/IMG_3006.jpg" width="1796" height="1020" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_3006.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_3006.jpg 1000w, https://ifwego.co/content/images/size/w1600/2024/05/IMG_3006.jpg 1600w, https://ifwego.co/content/images/2024/05/IMG_3006.jpg 1796w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/IMG_2981.jpg" width="1015" height="1353" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_2981.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_2981.jpg 1000w, https://ifwego.co/content/images/2024/05/IMG_2981.jpg 1015w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/IMG_3001.jpg" width="1015" height="1353" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_3001.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_3001.jpg 1000w, https://ifwego.co/content/images/2024/05/IMG_3001.jpg 1015w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Soaking green coffee in water causes them to germinate. Even though the seed is able to eject its embryo, it&apos;s not a guarantee of viability.</span></p></figcaption></figure><p>For a moment I thought that I might actually succeed in getting a live plant out of this process given that I had over a hundred germinated seeds, however it takes a long time to go from germination to an established plant. The embryo needs to grow big enough to reach the soil, put down roots, and then grow a sprout strong enough to lift the seed off the ground. That can takes months.</p><p>Tom mentions that you should plant the seeds 1.5&#x201D; deep in a pot that has space for a taproot (which adds up to a pretty deep pot). This tracks with the photos James Hoffman has in <a href="https://www.theworldatlasofcoffee.com/?ref=ifwego.co" rel="noreferrer">World Atlas of Coffee</a> (on page ~18?) where there&#x2019;s a grid of deep pots. The tree I got from FRINJ came in a roughly 4x4 by 12&#x201D; deep nursery pot.</p><p>Thinking back to the seeds I started from parchment, I wondered if this had something to do with why a handful of my &#x201C;soldiers&#x201D; (seedlings at the &#x201C;bean on a pole&#x201D; stage) died. They&#x2019;d start to grow black from where the embryo exits the seed and progressively blacken down the stem until the whole seed was drooping and the stem withered off. I started mine in seeding media: peat pellets and cardboard cells that you&#x2019;re supposed to be able to directly transplant into the ground and let the roots grow through them. I think some of the plants did grow roots through the starting media when I repotted them into 4&#x201D; round x ~6&#x201D; deep nursery pots, but I wonder if some of the ones that died did so because they failed to grow through the cardboard/webbing at the bottom of the starter media.</p><p>The alternative theory is that the seeds got too wet during this stage and started to mold internally. I noticed that the ones that turned black and withered were less far along in escaping the bean. For one of them, I think watering it accelerated its death. I saw a speck of black on the stem and thought maybe if I sprayed water on the bean it&#x2019;d help soften the parchment and outer layers, but instead it just died after a few hours.</p><p>So for this experiment, I decided to put them in a large seeding tray with a cover. I still wanted to be able to see the seeds so I could monitor their progress, but the large tray had a cover and would retain moisture better, hopefully reducing the amount of time I&apos;d have to spend keeping them moist.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/05/IMG_2988.jpg" class="kg-image" alt="Three Swings at Growing Coffee" loading="lazy" width="1015" height="1353" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_2988.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_2988.jpg 1000w, https://ifwego.co/content/images/2024/05/IMG_2988.jpg 1015w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Germinated green coffee laying in the big seeding tray.</span></figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/05/IMG_3011.jpg" class="kg-image" alt="Three Swings at Growing Coffee" loading="lazy" width="1804" height="1353" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_3011.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_3011.jpg 1000w, https://ifwego.co/content/images/size/w1600/2024/05/IMG_3011.jpg 1600w, https://ifwego.co/content/images/2024/05/IMG_3011.jpg 1804w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">By the time I was done, I had probably around 100 germinated seeds in the tray.</span></figcaption></figure><p>Things started going poorly almost immediately. After a day or two, some seeds were turning a dark blue-green color and getting slimey.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/05/IMG_3013.jpg" class="kg-image" alt="Three Swings at Growing Coffee" loading="lazy" width="1015" height="1353" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_3013.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_3013.jpg 1000w, https://ifwego.co/content/images/2024/05/IMG_3013.jpg 1015w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">One seed has an almost emerald green color, and several others have splotches of blue-green.</span></figcaption></figure><p>I kept them for a few weeks over the winter holidays, misting them occasionally. By the time New Year&apos;s rolled around, they were covered in mold. I&apos;ll spare you the photos &#x2013; they&apos;re upsetting. I threw out the whole batch.</p><p>If I tried it again, I think I&apos;d do it like Tom recommended and bury the germinated seedlings. That way, when some start molding, the spores are contained in the soil instead of being free to float around and infect other seeds. I&apos;d also start with fresher green coffee than the ones I used.</p><p>I suspect that if I had been more selective with the seeds and only focused on the most viable ones (e.g. the ones that germinated fastest, had the best shape, least discoloration, etc.), I might have been able to keep them alive for longer. However, I didn&apos;t have time to turn this experiment into a project, so I had to leave it there.</p><h2 id="viability">Viability</h2><p>While my seedlings were dying, I found a study from 2007 evaluating the viability of growing plants from green coffee.</p><blockquote>In all samples that were hulled prior to storage, <strong>&gt;50 % of the seeds lost viability within the first 3 months of storage. After 6 months, the fraction of seeds still viable was &lt;10 %. After 1 year of storage, all seeds were dead</strong> (<a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2701840/figure/MCM277F2/?ref=ifwego.co"><u>Fig.&#xA0;2</u></a>). The mode of processing had no effect on the time course of viability loss. In contrast, the so-called &#x2018;pergamino&#x2019; or &#x2018;parchment coffees&#x2019; (German: &#x2018;Hornschalenkaffee&#x2019;) revealed a different pattern. These coffee beans were stored within the parchment (endocarp) and, although these samples were derived from the same processing batch as the corresponding wet-processed samples, the loss of viability was quite different. <strong>Even after a longer storage period than 1 year, more than half of the beans stored as parchment coffee were still alive</strong> (<a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2701840/figure/MCM277F2/?ref=ifwego.co"><u>Fig.&#xA0;2</u></a>). </blockquote><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4C4;</div><div class="kg-callout-text"><i><em class="italic" style="white-space: pre-wrap;">Selmar D, Bytof G, Knopp SE. The storage of green coffee (Coffea arabica): decrease of viability and changes of potential aroma precursors. Ann Bot. 2008 Jan;101(1):31-8. doi: 10.1093/aob/mcm277. Epub 2007 Nov 2. PMID: 17981880; PMCID: PMC2701840.</em></i></div></div><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2701840/?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">The Storage of Green Coffee (Coffea arabica): Decrease of Viability and Changes of Potential Aroma Precursors</div><div class="kg-bookmark-description">When green coffee is stored for a prolonged time the coffee quality decreases distinctively. Apart from well-known &#x2018;off-notes&#x2019; that arise from undesired oxidations of lipids, a typical &#x2018;flattening&#x2019; of the cup quality is&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.ncbi.nlm.nih.gov/coreutils/nwds/img/favicons/favicon-192.png" alt="Three Swings at Growing Coffee"><span class="kg-bookmark-author">PubMed Central (PMC)</span><span class="kg-bookmark-publisher">Dirk Selmar</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.ncbi.nlm.nih.gov/corehtml/pmc/pmcgifs/pmc-card-share.jpg?_=0" alt="Three Swings at Growing Coffee"></div></a></figure><p>These numbers approximately line up with the numbers Tom reported. Processing coffee can take 1-2 months, followed by potentially 1-2 months of transit. By the time the green coffee arrives, it could&apos;ve been stored for 2-4 months. According to figure 2 from the paper, the researchers found about 20% of seeds to be viable, which roughly matches up with Tom&apos;s germination rate of 25-30%. That said, Tom reported that of the ones that germinated, only about 1 in 50 successfully sprouted. That puts us in the range of about half a percent for seeds surviving, which the researchers estimate would happen after about 9-12 months. Coffee is considered &quot;past crop&quot; after about a year, so I doubt that Tom was trying to germinate coffee that&apos;s 9 months past harvest (especially since he&apos;s an importer and would have access to a constant supply of fresh coffee).</p><p>The paper had one other interesting note:</p><blockquote>Imbibition by coffee seeds that had died a number of months before resulted in a characteristic appearance. During imbibition by dead seeds, many, mainly brownish substances leaked out, due to the disintegration and decompartmentation of the seeds. In contrast, imbibition of seeds that had been dead for only a few days or weeks resulted in a quite different picture: 2&#x2013;3 d after imbibition, <strong>the dead seeds had acquired a dark blue-green colour</strong> (<a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2701840/figure/MCM277F3/?ref=ifwego.co"><u>Fig.&#xA0;3</u></a>A). <strong>The characteristic blue-green colour is known as viridinic acid</strong> (German: &#x2018;Viridins&#xE4;ure&#x2019;). This results from the oxidation of chlorogenic acids and subsequent reaction with primary amino compounds (<a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2701840/?ref=ifwego.co#MCM277C32">Watanabe<u>&#xA0;</u><em>et al.</em>, 1996</a>;&#xA0;<a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2701840/?ref=ifwego.co#MCM277C36">Yabuta<u>&#xA0;</u><em>et al.</em>, 2001</a>). Similar reactions are frequently visible as the result of injuries that occur during wet processing, especially in the course of mechanical pulping and demucilation (<a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2701840/figure/MCM277F3/?ref=ifwego.co"><u>Fig.&#xA0;3</u></a>B).<br><br>[...]<br><br>Direct contact of enzyme and substrates should be a precondition for such enzymatic oxidation; this may occur in the course of decompartmentation due to cell-death-related disintegration. As mentioned above, <strong>the formation of the blue-green compounds only took place in seeds that died within a short time period preceding evaluation. However, in seeds that had lost viability several months before imbibition, these oxidation-induced processes could not observed</strong>, although in all cases the tissues of the dead seeds would have disintegrated. Consequently, the enzymes that are responsible for the post-mortem oxidation of chlorogenic acids are still active in seeds that have died within a short period preceding imbibition. In contrast, these oxidative enzymes were already inactive in stale seeds because of denaturation processes that proceeded during the storage of dead seeds.</blockquote><p>This explained the blue-green color I was seeing on my seeds and in the serum from soaking the seeds: these seeds had died, but relatively recently. The green coffee I used had been imported some months ago, in addition to however long it had been sitting after processing and in transit. They hadn&apos;t been sitting around for years (which could&apos;ve deactivated the oxidative enzymes), but they&apos;d also had plenty of time to lose viability.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/IMG_3009.jpg" width="1015" height="1353" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_3009.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_3009.jpg 1000w, https://ifwego.co/content/images/2024/05/IMG_3009.jpg 1015w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/IMG_3004.jpg" width="1015" height="1353" loading="lazy" alt="Three Swings at Growing Coffee" srcset="https://ifwego.co/content/images/size/w600/2024/05/IMG_3004.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/05/IMG_3004.jpg 1000w, https://ifwego.co/content/images/2024/05/IMG_3004.jpg 1015w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Close up shots of the serum, showing blue-green viridinic acid. The first picture is front-lit against a white bag. The second is backlit, showing the gradient from blue-green to yellow-brown.</span></p></figcaption></figure><h2 id="conclusion">Conclusion</h2><p>Having tried a few different methods for acquiring a coffee plant here are my thoughts:</p><ul><li><strong>If you just want a decorative plant, just buy one.</strong> Many nurseries carry small coffee plants (around the size of my seedlings) that make good house plants.</li><li><strong>If you want a larger plant that might produce cherries, buy an established plant from a specialized coffee nursery or farm like FRINJ.</strong> This also ensures that you&apos;re getting a coffee plant of a specific, known variety. Many of the decorative coffee plants I&apos;ve seen from general purpose nurseries only specify that the plant is <em>coffea arabica</em> (the species level) and don&apos;t list a variety because it&apos;s not likely to get big enough to produce fruit if it&apos;s grown in a small pot. As an educational tool, I&apos;d want it to bear fruit, and it&apos;s better if I can explain what variety it is.</li><li><strong>If you plan to start from seed, buy seeds with the parchment (aka pergamino) layer still on.</strong> Unprocessed coffee seeds in the parchment are the most likely to be viable. You can get seeds in parchment of specific varieties from different nurseries and specialty seed shops. If you have a friend with a coffee tree, you can also ask for some cherry. Regardless of how you acquire your seeds, buckle up for a couple months of careful watering and waiting as you wait for them to push out of the ground.</li></ul><p>As for me, I&apos;m glad that I managed to get viable plants from the cherries I got at Boot. Those plants remind me of where I started this chapter of my coffee journey, and every time I see them, it reminds me of my instructors, my friends, and the memories I made taking classes there. Hopefully one day, my seedlings will grow into big trees that can produce their own cherries, and the cycle can continue.</p><h2 id="additional-links">Additional Links</h2><p>For an introduction to Tom from Sweet Maria&apos;s, I like this podcast episode he did talking about his views about coffee tourism, as well as an episode he did with Lucia Solis discussing it.</p><figure class="kg-card kg-embed-card kg-card-hascaption"><iframe style="border-radius: 12px" width="100%" height="152" title="Spotify Embed: Tourism-Travel-Coffee (Part 1)" frameborder="0" allowfullscreen allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy" src="https://open.spotify.com/embed/episode/1gz783tB7eTHqp1m96kVWW?si=2bdd6e7fe9024440&amp;utm_source=oembed"></iframe><figcaption><p><span style="white-space: pre-wrap;">For an introduction to Tom, I like this podcast episode he did about coffee tourism.</span></p></figcaption></figure><figure class="kg-card kg-embed-card kg-card-hascaption"><iframe style="border-radius: 12px" width="100%" height="152" title="Spotify Embed: #54: Visiting Producers, Advanced Tourism &amp; The Coffee Hunter with Tom of Sweet Maria&#x2019;s" frameborder="0" allowfullscreen allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy" src="https://open.spotify.com/embed/episode/0zmZh7Rj3pjs2wTyKV65r5?si=5de9874d80284f9a&amp;utm_source=oembed"></iframe><figcaption><p><span style="white-space: pre-wrap;">And also this episode by Lucia Solis chatting with Tom about that episode.</span></p></figcaption></figure>]]></content:encoded></item><item><title><![CDATA[Making D&D Character Portraits With ChatGPT]]></title><description><![CDATA[<p>Alright, this one&apos;s pretty nerdy, even by my standards. Brace yourselves.</p><p>I feel like I should mention up top that I have cautious feelings about the impact of generative AI on the livelihoods and esteem of artists who have traditionally designed and illustrated characters for tabletop games. Also,</p>]]></description><link>https://ifwego.co/making-d-d-character-portraits/</link><guid isPermaLink="false">663e6b6e40e0de000184e291</guid><category><![CDATA[Tech]]></category><category><![CDATA[Art]]></category><category><![CDATA[Games]]></category><dc:creator><![CDATA[Daniel Pok]]></dc:creator><pubDate>Thu, 16 May 2024 18:38:40 GMT</pubDate><media:content url="https://ifwego.co/content/images/2024/05/DALL-E-2024-05-09-23.46.18---Illustration-of-a-young-male-paladin-named-Callum--around-18-or-19-years-old--with-a-serious-expression-due-to-recent-loss.-He-wears-medieval-fantasy-.webp" medium="image"/><content:encoded><![CDATA[<img src="https://ifwego.co/content/images/2024/05/DALL-E-2024-05-09-23.46.18---Illustration-of-a-young-male-paladin-named-Callum--around-18-or-19-years-old--with-a-serious-expression-due-to-recent-loss.-He-wears-medieval-fantasy-.webp" alt="Making D&amp;D Character Portraits With ChatGPT"><p>Alright, this one&apos;s pretty nerdy, even by my standards. Brace yourselves.</p><p>I feel like I should mention up top that I have cautious feelings about the impact of generative AI on the livelihoods and esteem of artists who have traditionally designed and illustrated characters for tabletop games. Also, none of this is endorsed by the companies who make the tools I used. At the professional level, I  favor having artists involved, and I hope we find good ways to integrate these tools into their arsenal. However, I happen to be artistically challenged, and I can&apos;t visualize characters without some help, so my table and I spent some time trying to get inspiration from ChatGPT.</p><p>We ended up with this process that produced pretty cool art that matched our (developing) vision for our characters in an impressive if sometimes inconsistent way.</p><p>The tl;dr is this:</p><ol><li>Start with some visual inspiration, either art found online or AI generated portraits.</li><li>Use that art as a reference for making a 3D model using <a href="https://www.heroforge.com/?ref=ifwego.co" rel="noreferrer">Hero Forge</a> (a character builder tool for making D&amp;D minis).</li><li>Tweak the pose, equipment, and colors of the 3D model in Hero Forge, then have ChatGPT translate those screenshots into high quality character art.</li></ol><p>Looking at that list, you may be wondering why I ran the art back though ChatGPT if I started out with an AI generated portrait as inspiration. The main reason is control over details. When having AI generate the image purely from a text description, it has very few constraints which makes it hard to make small tweaks without ending up with a very different style. For example, you might want to change the character&apos;s pose or what weapon they&apos;re holding, or the color of a specific item. It&apos;s possible to do this iteratively by selectively regenerating parts of the image, but this is time consuming and cumbersome, and I think that it tends to drift away from your original style over time. By giving the AI a reference image and asking it to target a certain art style, I found it easier to control the details of the character.</p><h2 id="step-1-visual-inspiration">Step 1: Visual Inspiration</h2><ul><li>For some characters, we used existing art that we found online as a placeholder character portrait. This is common, especially for NPCs. None of us have a strong sense of what these characters look like, so it&apos;s easy to find something acceptable and just move on.</li><li>For characters that had established visual elements or that were important enough for us to develop custom looks for, we used ChatGPT interactively to discuss the characters, and it generated concept art for us using its integration with DALL-E.</li></ul><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/DALL-E-2024-04-19-18.18.55---A-high-fantasy-depiction-of-a-bald-vampire-with-smaller--intense-black-eyes-and-more-pronounced--angular-facial-features--set-in-the-enchanting-forest-1.png" width="1024" height="1024" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT" srcset="https://ifwego.co/content/images/size/w600/2024/05/DALL-E-2024-04-19-18.18.55---A-high-fantasy-depiction-of-a-bald-vampire-with-smaller--intense-black-eyes-and-more-pronounced--angular-facial-features--set-in-the-enchanting-forest-1.png 600w, https://ifwego.co/content/images/size/w1000/2024/05/DALL-E-2024-04-19-18.18.55---A-high-fantasy-depiction-of-a-bald-vampire-with-smaller--intense-black-eyes-and-more-pronounced--angular-facial-features--set-in-the-enchanting-forest-1.png 1000w, https://ifwego.co/content/images/2024/05/DALL-E-2024-04-19-18.18.55---A-high-fantasy-depiction-of-a-bald-vampire-with-smaller--intense-black-eyes-and-more-pronounced--angular-facial-features--set-in-the-enchanting-forest-1.png 1024w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/DALL-E-2024-05-10-11.57.10---A-final-portrait-refinement-of-a-male-Aasimar-character-in-his-early-thirties-for-a-tabletop-role-playing-game.-The-character-has-dark-brown-hair--tie.webp" width="1024" height="1792" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT" srcset="https://ifwego.co/content/images/size/w600/2024/05/DALL-E-2024-05-10-11.57.10---A-final-portrait-refinement-of-a-male-Aasimar-character-in-his-early-thirties-for-a-tabletop-role-playing-game.-The-character-has-dark-brown-hair--tie.webp 600w, https://ifwego.co/content/images/size/w1000/2024/05/DALL-E-2024-05-10-11.57.10---A-final-portrait-refinement-of-a-male-Aasimar-character-in-his-early-thirties-for-a-tabletop-role-playing-game.-The-character-has-dark-brown-hair--tie.webp 1000w, https://ifwego.co/content/images/2024/05/DALL-E-2024-05-10-11.57.10---A-final-portrait-refinement-of-a-male-Aasimar-character-in-his-early-thirties-for-a-tabletop-role-playing-game.-The-character-has-dark-brown-hair--tie.webp 1024w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">ChatGPT/DALL-E generated these portraits. These characters are (from top to bottom or left to right depending on your screen size): Bud and Gliran.</span></p></figcaption></figure><p>This is the starting prompt I used:</p><blockquote>Hey can you help me design a character for D&amp;D? I&apos;ve been playing this character for a while but I don&apos;t know what he looks like. I&apos;m going to tell you a bit about him, and then I&apos;d like you to ask me questions to refine how he looks. Finally, I&apos;ll ask you to generate some images based on our conversation. Sound good?</blockquote><p>I think just saying it was supposed to be a character portrait for D&amp;D did a lot of heavy lifting in terms of establishing the visual style. It implies things like &quot;fantasy&quot;, &quot;painterly&quot;, &quot;portrait framing&quot;, etc.</p><p>The results were helpful in three ways:</p><ol><li>Chatting with ChatGPT about the character&apos;s appearance helped us develop them iteratively. For someone who doesn&apos;t think visually about characters, it was helpful to have the AI prompt me back with questions about details.</li><li>Even though the generated art doesn&apos;t perfectly capture the tone of the characters or could contain extraneous details that are hard to remove, it looks nice. It&apos;s evocative and makes a character sheet/token feel more personal. The goal of the art is to aid in roleplaying and immersion, so it doesn&apos;t have to be perfect, it just has to set the tone and remind us of key details about the characters.</li><li>The faces are detailed and unique. Ok, this one is more subtle, but it&apos;s important for the next step. Interactive character builders like the one we used to create 3D miniatures (&quot;minis&quot;) of the characters are like videogame character creation screens. There&apos;s often a limited number of stock faces and body parts to use as a base, so often characters will look same-y. The tool we used, Hero Forge, has very detailed customization controls that affect the face mesh, but since I&apos;m not a sculptor, I can&apos;t use them right without a reference image. That&apos;s where the generated art comes in: the faces are pretty unique and give me a target to aim for.</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/05/9770F631-C1A8-453B-9CDE-D340D79567D2.JPG" class="kg-image" alt="Making D&amp;D Character Portraits With ChatGPT" loading="lazy" width="1284" height="2282" srcset="https://ifwego.co/content/images/size/w600/2024/05/9770F631-C1A8-453B-9CDE-D340D79567D2.JPG 600w, https://ifwego.co/content/images/size/w1000/2024/05/9770F631-C1A8-453B-9CDE-D340D79567D2.JPG 1000w, https://ifwego.co/content/images/2024/05/9770F631-C1A8-453B-9CDE-D340D79567D2.JPG 1284w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Ok, so I was a sculptor for about 2 hours last time I visited Paris and took a bust sculpting class. That was a lot of fun, and I&apos;d recommend it.</span></figcaption></figure><h2 id="step-2-turning-it-into-3d-modeled-minis">Step 2: Turning It Into 3D Modeled Minis</h2><p>So the next step was to use the reference images to create models in Hero Forge.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/05/Screenshot-2024-05-10-at-12.25.05-PM.png" class="kg-image" alt="Making D&amp;D Character Portraits With ChatGPT" loading="lazy" width="2000" height="1152" srcset="https://ifwego.co/content/images/size/w600/2024/05/Screenshot-2024-05-10-at-12.25.05-PM.png 600w, https://ifwego.co/content/images/size/w1000/2024/05/Screenshot-2024-05-10-at-12.25.05-PM.png 1000w, https://ifwego.co/content/images/size/w1600/2024/05/Screenshot-2024-05-10-at-12.25.05-PM.png 1600w, https://ifwego.co/content/images/2024/05/Screenshot-2024-05-10-at-12.25.05-PM.png 2000w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">This is what the editor looks like in Hero Forge. It&apos;s like a video game character creator where you can pick your character&apos;s hair, stature, equipment, etc. However, it also lets you customize their faces in great detail and add colors to items.</span></figcaption></figure><p>Hero Forge has a set of stock face shapes. They tend to be a bit cartoonish, (probably to make them easier to see when they&apos;re on a 1&quot; tall mini), and if you over use the same ones, characters won&apos;t look very unique. Humans are very sensitive to the geometry of faces, so even small tweaks can lead to unique looking characters. For that, I delved into the advanced tools in the Hero Forge face editor and tweaked the dimensions of each part of these characters&apos; facial anatomy until they were close to the reference images.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/05/Screenshot-2024-05-10-at-12.20.38-PM.png" class="kg-image" alt="Making D&amp;D Character Portraits With ChatGPT" loading="lazy" width="2000" height="1152" srcset="https://ifwego.co/content/images/size/w600/2024/05/Screenshot-2024-05-10-at-12.20.38-PM.png 600w, https://ifwego.co/content/images/size/w1000/2024/05/Screenshot-2024-05-10-at-12.20.38-PM.png 1000w, https://ifwego.co/content/images/size/w1600/2024/05/Screenshot-2024-05-10-at-12.20.38-PM.png 1600w, https://ifwego.co/content/images/2024/05/Screenshot-2024-05-10-at-12.20.38-PM.png 2000w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">The editor lets you adjust the dimensions and position of most facial features. It&apos;s actually incredibly detailed under the hood, letting you adjust things like the eye bags, eyelids, and epicanthic folds separately.</span></figcaption></figure><p>Because I backed one of their Kickstarters, I have access to a Hero Forge feature that lets me make spinny GIFs of the models. Take a look:</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/32-turnaround--1-.gif" width="512" height="512" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/32-turnaround--4-.gif" width="512" height="512" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Here&apos;s Bud. He has a necromancer thing going on right now and he picked up some ...let&apos;s call it &quot;edgy&quot;... armor recently.</span></p></figcaption></figure><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/Gliran-turnaround--2-.gif" width="512" height="512" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/Gliran-turnaround--3-.gif" width="512" height="512" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Here&apos;s my dude, Gliran. He had kinda an awkward expression in the concept art, and I tried my best to capture it. The material in his left eye and the blade of the BFS (Big ___ing Sword) are supposed to be translucent. I ordered a color mini from Hero Forge, so we&apos;ll see how well it translates to the 3D print.</span></p></figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/05/Gliran-turnaround.gif" class="kg-image" alt="Making D&amp;D Character Portraits With ChatGPT" loading="lazy" width="512" height="512"><figcaption><span style="white-space: pre-wrap;">One more spinny for Gliran b/c I thought it looked cool.</span></figcaption></figure><p>And introducing some new characters:</p><p>This is Callum, an NPC who became an Oath of Glory Paladin through the tragic loss of three of his friends in an ill-fated encounter with a fire elemental. We started with art from internet for him since prior to the encounter with the elemental he was a minor NPC. As a result, I don&apos;t have his source art to share.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/Callum-turnaround--1-.gif" width="512" height="512" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/Callum-turnaround--2-.gif" width="512" height="512" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">This is Callum. I tried to get his face shape right based on the reference art. However, despite the insane amount of facial customization, Hero Forge doesn&apos;t have tools for customizing hair, so everything has a sorta Lego-hair look to it. It&apos;s probably due to constraints around what is 3D printable. Hair (thin, lofted shapes) can be really hard to print if you&apos;re not careful.</span></p></figcaption></figure><p>...well once our characters (Bud and Gliran) heard about the demise of the rest of the NPCs (Callum&apos;s friends), we had to go clean up after them, so we went to fight the elemental. That fight felt so unwinnable that we turned to superstition.</p><p><a href="https://www.youtube.com/watch?v=87F-Ind9BaQ&amp;list=PLuNLwLRy7g-hTIUJBnf0NNN7SrAvfaCoD&amp;ref=ifwego.co" rel="noreferrer">See, the dice give and they take away. They&apos;re fickle.</a> But they have one weakness: reverse psychology. If we wanted our characters to live, they&apos;d definitely die, and we had just invested a few hours in developing art for them, so the dice knew we were invested. Not good. To convince them otherwise, we devised a scheme:</p><ul><li>My friend asked me to come up with the class and lineage of his next character. Knowing him, I picked a combo that he&apos;d never pick for himself: a genasi bard. Genasi are half-elemental people, so like fire hair, or water hair, or wind hair, or crystals for hair. (Sounds like lazy writing when I say it out that way, doesn&apos;t it?) Turns out he had never even opened the players handbook to the bard page, so mission accomplished. </li><li>Then, during the fight, every time he had to roll for something important, I&apos;d whip out Hero Forge and start working on a model for the genasi bard while we loudly proclaimed how much more fun it would be to play that character than the ones we were playing now. Because of course, the only way to convince the dice that we were even more serious about this character than the ones we just made art for was to pretend to make and buy a physical mini, the next level of commitment.</li></ul><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/Genasi-Bard-turnaround.gif" width="512" height="512" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/Genasi-Bard-turnaround--1-.gif" width="512" height="512" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">That&apos;s where we got this guy, the genasi bard.</span></p></figcaption></figure><p>By making the 3D models, we could control the details of their faces, items, colors, and motifs. Working in Hero Forge can feel unconstrained in a different way, where without reference art, it&apos;s easy to make unnatural looking faces by playing with all the sliders. Starting with some generated art fixes that. On the other hand, it&apos;s a lot easier to experiment in Hero Forge when customizing the way the character feels because you get immediate visual feedback when adding items, changing body proportions, tweaking their stance, and fiddling with colors.</p><h2 id="step-3-pose-the-models-and-regenerate-the-art">Step 3: Pose The Models and Regenerate the Art</h2><p>I tried to constrain the type of image that got generated by starting with a posed image of the mini against an appropriate background.</p><p>Let&apos;s start with Callum.</p><p>Here&apos;s the prompt I gave ChatGPT:</p><blockquote>I have a 3D rendered character token for a D&amp;D character, but I&apos;d like help generating a more realistic, illustrated portrait of him. His name is Callum and he is an Oath of Glory paladin. He&apos;s 18 or 19 years old, and despite his youth, he&apos;s become more serious lately due to the loss of some friends. </blockquote><p>And this is what it gave me:</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/6d969c6b-b83b-4f39-b113-b1234ebc17c0.png" width="2000" height="2000" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT" srcset="https://ifwego.co/content/images/size/w600/2024/05/6d969c6b-b83b-4f39-b113-b1234ebc17c0.png 600w, https://ifwego.co/content/images/size/w1000/2024/05/6d969c6b-b83b-4f39-b113-b1234ebc17c0.png 1000w, https://ifwego.co/content/images/size/w1600/2024/05/6d969c6b-b83b-4f39-b113-b1234ebc17c0.png 1600w, https://ifwego.co/content/images/2024/05/6d969c6b-b83b-4f39-b113-b1234ebc17c0.png 2048w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/DALL-E-2024-05-09-23.46.18---Illustration-of-a-young-male-paladin-named-Callum--around-18-or-19-years-old--with-a-serious-expression-due-to-recent-loss.-He-wears-medieval-fantasy--1.webp" width="1024" height="1024" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT" srcset="https://ifwego.co/content/images/size/w600/2024/05/DALL-E-2024-05-09-23.46.18---Illustration-of-a-young-male-paladin-named-Callum--around-18-or-19-years-old--with-a-serious-expression-due-to-recent-loss.-He-wears-medieval-fantasy--1.webp 600w, https://ifwego.co/content/images/size/w1000/2024/05/DALL-E-2024-05-09-23.46.18---Illustration-of-a-young-male-paladin-named-Callum--around-18-or-19-years-old--with-a-serious-expression-due-to-recent-loss.-He-wears-medieval-fantasy--1.webp 1000w, https://ifwego.co/content/images/2024/05/DALL-E-2024-05-09-23.46.18---Illustration-of-a-young-male-paladin-named-Callum--around-18-or-19-years-old--with-a-serious-expression-due-to-recent-loss.-He-wears-medieval-fantasy--1.webp 1024w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Hero Forge screenshot on the left (or top), ChatGPT/DALL-E output on the right (or bottom). Callum got a bit of a glow-up.</span></p></figcaption></figure><p>And then for our edgy-boy, Bud:</p><blockquote>I have a 3D rendered character token for a D&amp;D character, but I&apos;d like help generating a more realistic, illustrated portrait of him. His name is Bud and he&apos;s a servant of the goddess of darkness. In this portrait, he&apos;s standing in a fiery volcano casting a dark spell. He&apos;s about 35 years old but his skin in a pallid white and his face is gaunt and bony, like a skull. His eyes are dark in their sockets and emit a faint aura of night.</blockquote><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/8e239615-c95c-453a-b0e2-75c760e1ce69.png" width="2000" height="2000" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT" srcset="https://ifwego.co/content/images/size/w600/2024/05/8e239615-c95c-453a-b0e2-75c760e1ce69.png 600w, https://ifwego.co/content/images/size/w1000/2024/05/8e239615-c95c-453a-b0e2-75c760e1ce69.png 1000w, https://ifwego.co/content/images/size/w1600/2024/05/8e239615-c95c-453a-b0e2-75c760e1ce69.png 1600w, https://ifwego.co/content/images/2024/05/8e239615-c95c-453a-b0e2-75c760e1ce69.png 2048w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/DALL-E-2024-05-10-13.04.09---A-realistic--illustrated-portrait-of-a-D-D-character-named-Bud.-He-s-standing-in-a-fiery-volcano--casting-a-dark-spell.-He-s-a-servant-of-the-goddess-.webp" width="1024" height="1024" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT" srcset="https://ifwego.co/content/images/size/w600/2024/05/DALL-E-2024-05-10-13.04.09---A-realistic--illustrated-portrait-of-a-D-D-character-named-Bud.-He-s-standing-in-a-fiery-volcano--casting-a-dark-spell.-He-s-a-servant-of-the-goddess-.webp 600w, https://ifwego.co/content/images/size/w1000/2024/05/DALL-E-2024-05-10-13.04.09---A-realistic--illustrated-portrait-of-a-D-D-character-named-Bud.-He-s-standing-in-a-fiery-volcano--casting-a-dark-spell.-He-s-a-servant-of-the-goddess-.webp 1000w, https://ifwego.co/content/images/2024/05/DALL-E-2024-05-10-13.04.09---A-realistic--illustrated-portrait-of-a-D-D-character-named-Bud.-He-s-standing-in-a-fiery-volcano--casting-a-dark-spell.-He-s-a-servant-of-the-goddess-.webp 1024w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Once again, first image is Hero Forge, second is ChatGPT/DALL-E.</span></p></figcaption></figure><p>The generated image didn&apos;t make his eyes as dark as I&apos;d wanted. In the lore, Bud has &quot;Eyes of the Void&quot;, and we&apos;re not sure if that means he has obsidian eyeballs or black-hole voids. I got ChatGPT to tweak just the eyes by selectively regenerating just that part of the image. Because so much of the image stayed the same, it was very consistent, keeping the details and motifs from the Hero Forge design.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/05/Screenshot-2024-05-10-at-1.05.04-PM.png" class="kg-image" alt="Making D&amp;D Character Portraits With ChatGPT" loading="lazy" width="2000" height="1152" srcset="https://ifwego.co/content/images/size/w600/2024/05/Screenshot-2024-05-10-at-1.05.04-PM.png 600w, https://ifwego.co/content/images/size/w1000/2024/05/Screenshot-2024-05-10-at-1.05.04-PM.png 1000w, https://ifwego.co/content/images/size/w1600/2024/05/Screenshot-2024-05-10-at-1.05.04-PM.png 1600w, https://ifwego.co/content/images/2024/05/Screenshot-2024-05-10-at-1.05.04-PM.png 2000w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Selecting an area of the image for DALL-E to regenerate limits how much the image can change when re-prompted.</span></figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/05/DALL-E-2024-05-10-01.44.37---A-realistic--illustrated-portrait-of-a-D-D-character-named-Bud.-He-s-standing-in-a-fiery-volcano--casting-a-dark-spell.-He-s-a-servant-of-the-goddess-.webp" class="kg-image" alt="Making D&amp;D Character Portraits With ChatGPT" loading="lazy" width="1024" height="1024" srcset="https://ifwego.co/content/images/size/w600/2024/05/DALL-E-2024-05-10-01.44.37---A-realistic--illustrated-portrait-of-a-D-D-character-named-Bud.-He-s-standing-in-a-fiery-volcano--casting-a-dark-spell.-He-s-a-servant-of-the-goddess-.webp 600w, https://ifwego.co/content/images/size/w1000/2024/05/DALL-E-2024-05-10-01.44.37---A-realistic--illustrated-portrait-of-a-D-D-character-named-Bud.-He-s-standing-in-a-fiery-volcano--casting-a-dark-spell.-He-s-a-servant-of-the-goddess-.webp 1000w, https://ifwego.co/content/images/2024/05/DALL-E-2024-05-10-01.44.37---A-realistic--illustrated-portrait-of-a-D-D-character-named-Bud.-He-s-standing-in-a-fiery-volcano--casting-a-dark-spell.-He-s-a-servant-of-the-goddess-.webp 1024w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">There&apos;s our edgy-boy. ChatGPT tweaked the eyes without disturbing the rest of the details.</span></figcaption></figure><h2 id="experimenting-with-changing-poses">Experimenting With Changing Poses</h2><p>One of my hopes was that by starting with 3D models that I could modify and re-pose, I could get more consistent portrayals of the same characters in different situations.</p><p>I started by editing Callum&apos;s model in Hero Forge to depict him at a bar, mourning the loss of his friends. This would be shortly before Bud and Gliran picked him back up.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/Callum-Sad-turnaround.gif" width="512" height="512" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/Callum-Sad-portrait.png" width="2000" height="2000" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT" srcset="https://ifwego.co/content/images/size/w600/2024/05/Callum-Sad-portrait.png 600w, https://ifwego.co/content/images/size/w1000/2024/05/Callum-Sad-portrait.png 1000w, https://ifwego.co/content/images/size/w1600/2024/05/Callum-Sad-portrait.png 1600w, https://ifwego.co/content/images/2024/05/Callum-Sad-portrait.png 2048w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">I wanted to capture how he was injured while escaping the fight, and that his equipment was badly damaged.</span></p></figcaption></figure><p>So I tried to get ChatGPT to generate a new image based on this, keeping in mind the awesome paladin illustration it did earlier:</p><blockquote>Can you make me another illustration with the same style based on another source image? This image shows Callum a few months earlier, grieving at a bar after losing his friends in a fight with a fire elemental. He&apos;s looking at his injured left hand. Please take care to match the image you just generated as closely as possible in terms of Callum&apos;s facial features.</blockquote><p>Here are a few of the results it created after several tweaking prompts attempting to get the visual style to match the paladin pic:</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/DALL-E-2024-05-10-13.15.54---Illustration-of-a-young-male-paladin-named-Callum--around-18-or-19-years-old--grieving-at-a-bar-after-losing-his-friends.-He-is-wearing-tattered-cloth.webp" width="1024" height="1024" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT" srcset="https://ifwego.co/content/images/size/w600/2024/05/DALL-E-2024-05-10-13.15.54---Illustration-of-a-young-male-paladin-named-Callum--around-18-or-19-years-old--grieving-at-a-bar-after-losing-his-friends.-He-is-wearing-tattered-cloth.webp 600w, https://ifwego.co/content/images/size/w1000/2024/05/DALL-E-2024-05-10-13.15.54---Illustration-of-a-young-male-paladin-named-Callum--around-18-or-19-years-old--grieving-at-a-bar-after-losing-his-friends.-He-is-wearing-tattered-cloth.webp 1000w, https://ifwego.co/content/images/2024/05/DALL-E-2024-05-10-13.15.54---Illustration-of-a-young-male-paladin-named-Callum--around-18-or-19-years-old--grieving-at-a-bar-after-losing-his-friends.-He-is-wearing-tattered-cloth.webp 1024w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/DALL-E-2024-05-10-13.15.58---Illustration-of-a-young-male-paladin-named-Callum--around-18-or-19-years-old--grieving-at-a-bar-after-losing-his-friends.-He-is-in-a-contemplative-pos.webp" width="1024" height="1024" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT" srcset="https://ifwego.co/content/images/size/w600/2024/05/DALL-E-2024-05-10-13.15.58---Illustration-of-a-young-male-paladin-named-Callum--around-18-or-19-years-old--grieving-at-a-bar-after-losing-his-friends.-He-is-in-a-contemplative-pos.webp 600w, https://ifwego.co/content/images/size/w1000/2024/05/DALL-E-2024-05-10-13.15.58---Illustration-of-a-young-male-paladin-named-Callum--around-18-or-19-years-old--grieving-at-a-bar-after-losing-his-friends.-He-is-in-a-contemplative-pos.webp 1000w, https://ifwego.co/content/images/2024/05/DALL-E-2024-05-10-13.15.58---Illustration-of-a-young-male-paladin-named-Callum--around-18-or-19-years-old--grieving-at-a-bar-after-losing-his-friends.-He-is-in-a-contemplative-pos.webp 1024w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/DALL-E-2024-05-10-13.16.10---Illustration-of-a-young-male-named-Callum--around-18-or-19-years-old--shortly-before-becoming-an-Oath-of-Glory-paladin.-He-looks-sorrowful-due-to-rece.webp" width="1024" height="1024" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT" srcset="https://ifwego.co/content/images/size/w600/2024/05/DALL-E-2024-05-10-13.16.10---Illustration-of-a-young-male-named-Callum--around-18-or-19-years-old--shortly-before-becoming-an-Oath-of-Glory-paladin.-He-looks-sorrowful-due-to-rece.webp 600w, https://ifwego.co/content/images/size/w1000/2024/05/DALL-E-2024-05-10-13.16.10---Illustration-of-a-young-male-named-Callum--around-18-or-19-years-old--shortly-before-becoming-an-Oath-of-Glory-paladin.-He-looks-sorrowful-due-to-rece.webp 1000w, https://ifwego.co/content/images/2024/05/DALL-E-2024-05-10-13.16.10---Illustration-of-a-young-male-named-Callum--around-18-or-19-years-old--shortly-before-becoming-an-Oath-of-Glory-paladin.-He-looks-sorrowful-due-to-rece.webp 1024w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">ChatGPT/DALL-E. Notice how the first pic keeps his pristine armor, even though the source screenshot from Hero Forge shows him in tattered clothes. I think I may have biased ChatGPT too much towards drawing from that image when really all I wanted was for it to keep his facial features and proportions consistent.</span></p></figcaption></figure><p>These results weren&apos;t as impressive as the original ones. The last image in the gallery wasn&apos;t bad, but even then it was clearly a different style. It sorta had that fan art look where many different artists draw the same character in different styles, but I wanted to do better.</p><p>So I did the only reasonable thing in this age of AI, and I asked ChatGPT to compare the really cool paladin image with the others, and asked it for help describing the difference between the styles. After a lot of back and forth (it was really obsessed with the backgrounds being different), we landed on some descriptors: &quot;photorealistic&quot;, &quot;painterly&quot;, and &quot;natural lighting&quot;.</p><p>After some reprompting using those keywords, this is what I got:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/05/DALL-E-2024-05-10-02.10.17---Photorealistic-illustration-of-a-young-male-named-Callum--around-18-or-19-years-old--shortly-before-becoming-an-Oath-of-Glory-paladin.-He-looks-sorrow.webp" class="kg-image" alt="Making D&amp;D Character Portraits With ChatGPT" loading="lazy" width="1024" height="1024" srcset="https://ifwego.co/content/images/size/w600/2024/05/DALL-E-2024-05-10-02.10.17---Photorealistic-illustration-of-a-young-male-named-Callum--around-18-or-19-years-old--shortly-before-becoming-an-Oath-of-Glory-paladin.-He-looks-sorrow.webp 600w, https://ifwego.co/content/images/size/w1000/2024/05/DALL-E-2024-05-10-02.10.17---Photorealistic-illustration-of-a-young-male-named-Callum--around-18-or-19-years-old--shortly-before-becoming-an-Oath-of-Glory-paladin.-He-looks-sorrow.webp 1000w, https://ifwego.co/content/images/2024/05/DALL-E-2024-05-10-02.10.17---Photorealistic-illustration-of-a-young-male-named-Callum--around-18-or-19-years-old--shortly-before-becoming-an-Oath-of-Glory-paladin.-He-looks-sorrow.webp 1024w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">ChatGPT / DALL-E. Poor Callum. Also, poor Callum&apos;s liver.</span></figcaption></figure><p>That&apos;s a lot better. Not quite perfect, but close enough in style and depiction to be useful.</p><h2 id="iterating-on-the-prompt">Iterating On the Prompt</h2><p>I decided to try this regeneration approach on one more character and decided it&apos;d be cool to see what it did with the genasi bard.</p><p>From talking to friends and experimenting a bit, these are the elements of the prompt that I wanted to include:</p><ul><li>Add stylistic notes: &quot;photorealistic&quot;, &quot;painterly style&quot;, &quot;natural lighting&quot;</li><li>Include a name and age, to get it to focus on making a more realistic person.</li><li>Some personality or biographical details, to let it fill in the blanks with tonally appropriate things.</li></ul><p>So my friends and I had to come up with some details for this bard on the fly since our characters did in fact survive the encounter with the fire elemental and the genasi bard had not yet seen service.</p><p>We named him Kalani, using <a href="https://www.reddit.com/r/d100/comments/sit0nq/d100_x4_genasi_names/?ref=ifwego.co" rel="noreferrer">a d100 table I found on Reddit</a>.</p><p>The prompt:</p><blockquote>I have a render of a D&amp;D mini and I&apos;d like you to help me generate a portrait based on it. The portrait should be done in a painterly style with photorealistic details and natural lighting. This character&apos;s name is Kalani and he is an air genasi bard, riding a griffin or a hippogriff. He&apos;s in his early 20&apos;s, and either as a consequence of his air genasi heritage or his youthful inexperience, he&apos;s pretty prone to seemingly random outbursts. He sometimes hears voices and music in the wind as it passes by. This leads him to sometimes vacillate between saying insightful or helpful things, and then doing more questionable things like egging on bar patrons, dishing hot takes, or making jokes that cut too close to truth. However, given his high charisma and good looks, he often gets a pass.</blockquote><p>The results:</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/42ca71ab-fd5d-4418-a902-68cea63e246d.png" width="2000" height="2000" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT" srcset="https://ifwego.co/content/images/size/w600/2024/05/42ca71ab-fd5d-4418-a902-68cea63e246d.png 600w, https://ifwego.co/content/images/size/w1000/2024/05/42ca71ab-fd5d-4418-a902-68cea63e246d.png 1000w, https://ifwego.co/content/images/size/w1600/2024/05/42ca71ab-fd5d-4418-a902-68cea63e246d.png 1600w, https://ifwego.co/content/images/2024/05/42ca71ab-fd5d-4418-a902-68cea63e246d.png 2048w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/05/DALL-E-2024-05-10-02.34.11---Create-a-portrait-of-an-air-genasi-bard-in-a-painterly-style-with-photorealistic-details-and-natural-lighting.-The-character-is-a-young-man-in-his-ear.webp" width="1024" height="1024" loading="lazy" alt="Making D&amp;D Character Portraits With ChatGPT" srcset="https://ifwego.co/content/images/size/w600/2024/05/DALL-E-2024-05-10-02.34.11---Create-a-portrait-of-an-air-genasi-bard-in-a-painterly-style-with-photorealistic-details-and-natural-lighting.-The-character-is-a-young-man-in-his-ear.webp 600w, https://ifwego.co/content/images/size/w1000/2024/05/DALL-E-2024-05-10-02.34.11---Create-a-portrait-of-an-air-genasi-bard-in-a-painterly-style-with-photorealistic-details-and-natural-lighting.-The-character-is-a-young-man-in-his-ear.webp 1000w, https://ifwego.co/content/images/2024/05/DALL-E-2024-05-10-02.34.11---Create-a-portrait-of-an-air-genasi-bard-in-a-painterly-style-with-photorealistic-details-and-natural-lighting.-The-character-is-a-young-man-in-his-ear.webp 1024w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">First pic is from Hero Forge, second from ChatGPT / DALL-E. I think his fan is made of feathers, which is awesome. Also, I think he gained an earring, which seems appropriate given his personality. He doesn&apos;t look particularly &quot;genasi&quot;, but there&apos;s actually a lot of room for how genasi are represented in D&amp;D, so it doesn&apos;t have to just be &quot;cloud hair&quot;. His mount looks like a falcon from this angle, but maybe its chimeric hind-quarters are just hidden behind Kalani.</span></p></figcaption></figure><p>Dang. Kalani&apos;s looking good on his first try. I legitimately want to see this character happen now.</p><h2 id="conclusion">Conclusion</h2><p>Yeah, that was fun. I&apos;m convinced that generative AI is a really great tool for inspiration and ideation, especially for folks like me that lack visual art skills. I think that if I had bigger plans for these characters, I&apos;d still want to commission an artist that could work with me to develop them further, both visually and as a character. That said, these characters feel so much more real when I can see them illustrated this way, knowing that they&apos;re custom and unique.</p><p>I think I&apos;ll try this technique again whenever I get a chance to make a new character (hopefully because I join a second campaign and not because Gliran kicks the bucket).</p><p></p>]]></content:encoded></item><item><title><![CDATA[Can we identify good locations for a specialty cafe using data?]]></title><description><![CDATA[<p>When my friends Mabel &amp; Ming from <a href="https://www.moonwakecoffeeroasters.com/?ref=ifwego.co" rel="noreferrer">Moonwake Coffee Roasters</a> (<a href="https://www.instagram.com/moonwakecoffeeroasters?ref=ifwego.co" rel="noreferrer">@moonwakecoffeeroasters</a>) told me they were looking to open a cafe, I started wondering whether we could use data to evaluate potential locations in the Bay Area that might be underserved by specialty cafes relative to their demand.  Unfortunately, my experiment</p>]]></description><link>https://ifwego.co/coffee-map/</link><guid isPermaLink="false">65f3bdc6702d0a000145def7</guid><category><![CDATA[Projects]]></category><category><![CDATA[Coffee]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Daniel Pok]]></dc:creator><pubDate>Tue, 16 Apr 2024 21:06:37 GMT</pubDate><media:content url="https://ifwego.co/content/images/2024/03/pop-isochrone-bay-area.png" medium="image"/><content:encoded><![CDATA[<img src="https://ifwego.co/content/images/2024/03/pop-isochrone-bay-area.png" alt="Can we identify good locations for a specialty cafe using data?"><p>When my friends Mabel &amp; Ming from <a href="https://www.moonwakecoffeeroasters.com/?ref=ifwego.co" rel="noreferrer">Moonwake Coffee Roasters</a> (<a href="https://www.instagram.com/moonwakecoffeeroasters?ref=ifwego.co" rel="noreferrer">@moonwakecoffeeroasters</a>) told me they were looking to open a cafe, I started wondering whether we could use data to evaluate potential locations in the Bay Area that might be underserved by specialty cafes relative to their demand.  Unfortunately, my experiment didn&apos;t work out, and I was unable to get good quality data that could support cafe owners in selecting a location. The good news is that Moonwake will open their first cafe in San Jose soon (later this year, fingers crossed!). Despite the lack of results, it was a fun exercise in simple data science and market research.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.mercurynews.com/2024/01/19/new-coffee-roaster-to-open-shop-in-san-jose/?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">New coffee roaster to open shop in San Jose</div><div class="kg-bookmark-description">Moonwake Coffee Roasters began in a place many well-known businesses around the Bay Area have started: a garage.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.mercurynews.com/wp-content/uploads/2016/10/32x32-mercury-news-white.png?w=32" alt="Can we identify good locations for a specialty cafe using data?"><span class="kg-bookmark-author">The Mercury News</span><span class="kg-bookmark-publisher">Kate Bradshaw</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.mercurynews.com/wp-content/uploads/2023/12/SJM-L-MOONWAKE-02.jpg?w=1024&amp;h=683" alt="Can we identify good locations for a specialty cafe using data?"></div></a></figure><p>This was my starting hypothesis:</p><blockquote>We can use free or cheaply acquirable data to identify whether a location in the Bay Area is over- or under-served by specialty coffee cafes.</blockquote><p>There are many new cafes opening in the Bay Area, and I think there&apos;s room for many more as we all collectively grow the market of people who appreciate specialty coffee. My hope was that we could show that with data, and help potential cafe owners identify areas where their cafes would make a big impact.</p><p>Let&apos;s jump straight to the results:</p><h2 id="it-didnt-work-because-i-couldnt-find-good-enough-data">It Didn&apos;t Work Because I Couldn&apos;t Find Good Enough Data</h2><p>I don&apos;t think that <em>free or cheaply available</em> data is capable of providing significant signal for potential cafe owners in the Bay Area, at least not in a way that&apos;s better than general-purpose market data for retail businesses. I found some market research firms that had reports about coffee, but they weren&apos;t cheap and they weren&apos;t specific.</p><p>I found national level trend data, but it&apos;s hard to apply nation-wide demographic breakdowns to a particular region. It might have been possible to look for census tracts in the Bay Area that roughly matched the demographics in the national studies, but it didn&apos;t seem like it would be high signal. I evaluated commissioning a survey, but the costs were too high for my little (unfunded) experiment:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/03/surveymonkey.png" class="kg-image" alt="Can we identify good locations for a specialty cafe using data?" loading="lazy" width="2000" height="1159" srcset="https://ifwego.co/content/images/size/w600/2024/03/surveymonkey.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/surveymonkey.png 1000w, https://ifwego.co/content/images/size/w1600/2024/03/surveymonkey.png 1600w, https://ifwego.co/content/images/2024/03/surveymonkey.png 2000w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Surveymonkey estimated it would cost $1200 to get 200 responses from adults in the Bay Area.</span></figcaption></figure><p>Here&apos;s what I would&apos;ve wanted to learn (though probably not in 5 questions):</p><ul><li>How many people brew their own coffee at home? How many brew specialty coffee from whole bean?</li><li>How many people order pour-overs at cafes? How often?</li><li>Which Bay Area specialty coffee brands are people aware of?</li><li>How far are they willing to drive for better coffee?</li><li>How much do they know (or care) about various characteristics of coffee such as where it&apos;s produced, the processing method, its flavor profile, its cupping score, roast freshness, certifications, etc.?</li></ul><p>I was only able to source general population demographic data from the US Census (via the American Community Survey), as well as some broader market research about coffee that was freely accessible:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.ncausa.org/Research-Trends/MarketResearch?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">NCA Coffee Market Research</div><div class="kg-bookmark-description">Get the information your business needs to succeed - from consumer insights to emerging coffee market trends.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.ncausa.org/Portals/56/nca-logo.ico" alt="Can we identify good locations for a specialty cafe using data?"></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.ncausa.org/portals/56/Images/MarketResearch/NCA_Web_Large_market-research-home.png" alt="Can we identify good locations for a specialty cafe using data?"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://sca.coffee/sca-news/2018/11/01/business-insights-from-square-coffee-data-report-retail-sentiment-index-rsi?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Business Insights from the Square Coffee Data Report and the Retail Sentiment Index (RSI) &#x2014; Specialty Coffee Association</div><div class="kg-bookmark-description">The independent coffee shop is an integral part of so many neighborhoods. It&#x2019;s a space where communities come together.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://images.squarespace-cdn.com/content/v1/584f6bbef5e23149e5522201/1481744254945-C67PKV7VRZ68MTSQ0FVO/favicon.ico?format=100w" alt="Can we identify good locations for a specialty cafe using data?"><span class="kg-bookmark-author">Specialty Coffee Association</span><span class="kg-bookmark-publisher">SCA Staff</span></div></div><div class="kg-bookmark-thumbnail"><img src="http://static1.squarespace.com/static/584f6bbef5e23149e5522201/5ddbc527ee3ebb607afc8849/5f3d24101823f969d83b9856/1614786844960/?format=1500w" alt="Can we identify good locations for a specialty cafe using data?"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://squareup.com/us/en/press/coffee-data-report-national-coffee-day?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Square and the Specialty Coffee Association Release Coffee Data Report in honor of National Coffee Day</div><div class="kg-bookmark-description"></div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://xms-production-f.squarecdn.com/xms/assets/public-web-styles/favicon-770e0889aefd823056c7cdbb066a445be0f0754c1b4d4cba877e120fdbcb63e6.ico" alt="Can we identify good locations for a specialty cafe using data?"><span class="kg-bookmark-author">Square</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://xms-production-f.squarecdn.com/xms/assets/public-web-styles/social/default-56f973ec4d9cb2927e20a0cb97201783e27bf352585fa25cddbde11aa81635a7.jpg" alt="Can we identify good locations for a specialty cafe using data?"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://intelligence.coffee/2023/07/scaling-beyond-specialty-coffee/?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Scaling beyond specialty coffee: Is the market smaller than the industry thinks? - Coffee Intelligence</div><div class="kg-bookmark-description">Despite the fact that the definition is more complex, &#x201C;specialty coffee&#x201D; as a concept is still largely used to refer to small, single-store owner-operated businesses rather than larger organisations.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://intelligence.coffee/wp-content/uploads/2022/02/cropped-CI-Icon-Logo-Transparent-270x270.png" alt="Can we identify good locations for a specialty cafe using data?"><span class="kg-bookmark-author">Coffee Intelligence</span><span class="kg-bookmark-publisher">Gabriella Oakley</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://intelligence.coffee/wp-content/uploads/2023/07/specialty-scaled2.jpg" alt="Can we identify good locations for a specialty cafe using data?"></div></a></figure><p><strong>However, the biggest revelation for me had nothing to do with the data: finding commercial real estate for your first cafe is highly idiosyncratic.</strong> There are a million other things to consider before the size of the specialty coffee market. Despite the commercial real-estate market&apos;s current woes, it&apos;s still hard to find a suitable location for the right price. Cafes are food service facilities and need spaces that can be renovated for their plumbing and electrical needs without breaking the bank. For a small business, proximity to the owners and their community is also a critical consideration. It&apos;s unlikely that a prospective cafe owner would open their first shop on the other side of the Bay just for the specialty market. That said, this data might be more useful for picking a second location: expanding to another location requires scaling the business in ways that make some of these proximity requirements moot, and the business has more time to consider where to expand to since they have one location that can continue generating revenue in the meantime.</p><h2 id="okay-but-what-about-these-maps">Okay, but what about these maps?</h2><p>I built a prototype map with some heuristics for supply and demand based on the data I got from the ACS and some manual data generation:</p><ul><li>I used population density and the density of high-income households (&gt;$200k annually) as proxies for demand.</li><li>I used isochrones representing a 10-minute driving distance radius as a proxy for cafe availability.</li></ul><div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text"><b><strong style="white-space: pre-wrap;">What&apos;s an &quot;isochrone&quot;?</strong></b><br><br>It&apos;s a shape on the map that represents how far you can get within a certain amount of time. For example, the isochrones on these maps show how far away you can be and still drive to a given cafe within 10 minutes. The roots of the word mean &quot;equal&quot; (&quot;iso-&quot;) and &quot;time&quot; (&quot;chrone&quot;).</div></div><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/03/draft-coffeemap-oak-berk.png" class="kg-image" alt="Can we identify good locations for a specialty cafe using data?" loading="lazy" width="800" height="800" srcset="https://ifwego.co/content/images/size/w600/2024/03/draft-coffeemap-oak-berk.png 600w, https://ifwego.co/content/images/2024/03/draft-coffeemap-oak-berk.png 800w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">This map shows overlapping isochrones (purple) surrounding a set of cafes (orange dots). Notice how most of Berkeley is only covered by a single isochrone (the one for Art&#xED;s Cafe), while San Francisco is blanketed in many overlapping isochrones for the many cafes there.</span></figcaption></figure><p>The first thing I did was cosplay as a Data Scientist and plop my data in a Python notebook via Google Colab. I came up with a list of fifty Bay Area cafes that I thought might count as specialty, manually geocoded them (got latitude/longitudes for them based on their address), used <a href="https://openrouteservice.org/dev/?ref=ifwego.co#/api-docs/v2/isochrones/{profile}/post" rel="noreferrer">Open Route Service</a> to generate the isochrones, and then used <a href="https://plotly.com/python/mapbox-county-choropleth/?ref=ifwego.co" rel="noreferrer">plotly</a> to make the maps with <a href="https://www.openstreetmap.org/?ref=ifwego.co#map=11/37.7848/-122.2964" rel="noreferrer">Open Street Map</a> styled tiles.</p><p>Here&apos;s what the resulting map looked like:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/03/draft-coffeemap-centralbay.png" class="kg-image" alt="Can we identify good locations for a specialty cafe using data?" loading="lazy" width="800" height="800" srcset="https://ifwego.co/content/images/size/w600/2024/03/draft-coffeemap-centralbay.png 600w, https://ifwego.co/content/images/2024/03/draft-coffeemap-centralbay.png 800w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">San Jose is cut off in this screenshot because of space, but it&apos;s pretty densely covered with cafes.</span></figcaption></figure><p>A couple things jumped out immediately:</p><p>On one hand, my data was really spotty. The list of cafes was incomplete, and everyone I showed the map to would ask &quot;<em>Why isn&apos;t XYZ cafe on this list?</em>&quot;. The definition of a &quot;specialty&quot; cafe is also vague and varies from person to person. I thought that fifty cafes would be a large enough sample, but in places where there are few cafes, the inclusion or exclusion of a single cafe can make an area look reasonably covered or extremely empty. For example, in Fremont, I didn&apos;t include Tamper Room (<a href="https://www.instagram.com/tamperroom/?ref=ifwego.co" rel="noreferrer">@tamperroom</a>) because they were pretty new at the time, but they&apos;re one of my favorite cafes in the Bay. If I had included them, Fremont would look a lot more covered (and if I were remaking the list today, I&apos;d definitely include them). Ditto for <a href="https://kaizenandcoffee.com/?ref=ifwego.co" rel="noreferrer">Kaizen and Coffee</a> in San Mateo (<a href="https://www.instagram.com/kaizenandcoffee/?hl=en&amp;ref=ifwego.co" rel="noreferrer">@kaizenandcoffee</a>)</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.instagram.com/tamperroom/?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Tamper Room (@tamperroom) &#x2022; Instagram photos and videos</div><div class="kg-bookmark-description">4,398 Followers, 455 Following, 82 Posts - See Instagram photos and videos from Tamper Room (@tamperroom)</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://static.cdninstagram.com/rsrc.php/v3/yG/r/De-Dwpd5CHc.png" alt="Can we identify good locations for a specialty cafe using data?"><span class="kg-bookmark-author">Instagram</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://scontent-sjc3-1.cdninstagram.com/v/t51.2885-19/326868010_707886354079886_7351956274321848992_n.jpg?stp=dst-jpg_s100x100&amp;_nc_cat=107&amp;ccb=1-7&amp;_nc_sid=3fd06f&amp;_nc_ohc=iPdAwDsVN-IAX872WjB&amp;_nc_ht=scontent-sjc3-1.cdninstagram.com&amp;oh=00_AfBqe4gsCMJMnirg3IT1Bv8u14PjCu-GyxZvPqz9ZqVS3Q&amp;oe=65F9B096" alt="Can we identify good locations for a specialty cafe using data?"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.instagram.com/kaizenandcoffee/?hl=en&amp;ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Kaizen &amp; Coffee (@kaizenandcoffee) &#x2022; Instagram photos and videos</div><div class="kg-bookmark-description">2,283 Followers, 67 Following, 52 Posts - See Instagram photos and videos from Kaizen &amp; Coffee (@kaizenandcoffee)</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://static.cdninstagram.com/rsrc.php/v3/yG/r/De-Dwpd5CHc.png" alt="Can we identify good locations for a specialty cafe using data?"><span class="kg-bookmark-author">Instagram</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://scontent-sjc3-1.cdninstagram.com/v/t51.2885-19/148603704_421141702452518_5198954231766508527_n.jpg?stp=dst-jpg_s100x100&amp;_nc_cat=105&amp;ccb=1-7&amp;_nc_sid=3fd06f&amp;_nc_ohc=-Tk2YCQI4agAX8Kjc-d&amp;_nc_ht=scontent-sjc3-1.cdninstagram.com&amp;oh=00_AfBeQAGo5t7dwRa4BCYqOZvTcEE_fLUqC9knN7RAG3L7xA&amp;oe=65F7D929" alt="Can we identify good locations for a specialty cafe using data?"></div></a></figure><p>On the other hand, it&apos;s clear that there are pockets of the Bay Area that are dense with cafes, and others that that have very few. Even accounting for my incomplete list of cafes, there&apos;s a stark difference between Dublin (all the way to the right, middle) and Palo Alto.</p><p>Knowing where existing cafes are is only half the equation though: my goal was to find places where demand for specialty would exceed supply, which meant that I needed a proxy for demand. As I mentioned before, I didn&apos;t have access to granular coffee demand data by locale, so I used population and income data from the <a href="https://www.census.gov/programs-surveys/acs?ref=ifwego.co" rel="noreferrer">American Communities Survey</a> as a very blunt heuristic. The ACS is conducted by the Census Bureau each year. It is less precise, but more responsive than the Census, which is run every 10 years. ACS data is also presented in five year rollups, which gives more granular data than the annual ACS, but less than the decennial Census. I used the five year rollups of ACS data from 2016-2021 for my visualizations.</p><p>To plot the data, I used <a href="https://leafletjs.com/?ref=ifwego.co" rel="noreferrer">Leaflet.js</a>, a Javascript library for rendering maps and plotting data on top of them. But to get the data ready to plot, I first had to transform the massive Census data CSVs into GeoJSON layers that Leaflet could render.</p><p>I used a data tool called DuckDB to do the data munging necessary to get the census data into the right format. I&apos;ve included some details on that process at the end of the article.</p><p>The result was a giant JSON file that had many entries like this for each census tract and zip code:</p><pre><code class="language-JSON">{
  &quot;type&quot;: &quot;Feature&quot;,
  &quot;properties&quot;: {
    &quot;geo_id&quot;: &quot;1400000US06085504321&quot;,
    &quot;census_tract_name&quot;: &quot;Census Tract 5043.21, Santa Clara County, California&quot;,
    &quot;land_area&quot;: 1450237,
    &quot;population&quot;: 5511,
    &quot;per_sq_km&quot;: 3800.068540521308,
    &quot;median_household_income&quot;: 146941,
    &quot;high_income_households&quot;: 448,
    &quot;households&quot;: 1675,
    &quot;high_income_per_sq_km&quot;: 308.91502561305498,
    &quot;perc_high_income&quot;: 0.26746268656716415,
    &quot;median_household_income:1&quot;: 146941
  },
  &quot;geometry&quot;: {
    &quot;type&quot;: &quot;Polygon&quot;,
    &quot;coordinates&quot;: [
      [
        [
          -121.875559,
          37.39924
        ],
        [
          -121.875352,
          37.399076
        ],
        &quot;...&quot;
      ]
    ]
  }
}
</code></pre><h2 id="lets-make-some-maps">Let&apos;s Make Some Maps</h2><p>Finally, with all the pieces together, I got a map:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/03/pop-isochrone-map-bay-area.png" class="kg-image" alt="Can we identify good locations for a specialty cafe using data?" loading="lazy" width="925" height="858" srcset="https://ifwego.co/content/images/size/w600/2024/03/pop-isochrone-map-bay-area.png 600w, https://ifwego.co/content/images/2024/03/pop-isochrone-map-bay-area.png 925w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Blue is population density, red is cafe isochrone coverage.</span></figcaption></figure><p>I could toggle layers on and off to include or exclude the &quot;base map&quot; (that&apos;s the Open Street Map) and overlays for population density and high-income household density by census tract or zip code. Removing the base map reduces the visual noise, so I toggled it off for most of these graphics.</p><p>The map below shows population density in blue, and cafe isochrone coverage in red. The darker the blue, the higher the population density, and the darker the red, the higher the number of specialty cafes nearby (from my list of 50).</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://ifwego.co/content/images/2024/03/pop-isochrone-bay-area-1.png" class="kg-image" alt="Can we identify good locations for a specialty cafe using data?" loading="lazy" width="2000" height="1295" srcset="https://ifwego.co/content/images/size/w600/2024/03/pop-isochrone-bay-area-1.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/pop-isochrone-bay-area-1.png 1000w, https://ifwego.co/content/images/size/w1600/2024/03/pop-isochrone-bay-area-1.png 1600w, https://ifwego.co/content/images/size/w2400/2024/03/pop-isochrone-bay-area-1.png 2400w" sizes="(min-width: 1200px) 1200px"></figure><p>At this point, all I could say is that, from eyeballing the map, the density of coffee shops seemed to be correlated with population density. There are some spots of higher density in the East Bay from Oakland to Hayward that aren&apos;t as well covered by cafes, but for the most part, the cafes are in the Bay&apos;s major population centers: San Francisco, San Jose, Oakland, and the mid-Peninsula.</p><p>But population density isn&apos;t the only metric we can get from the ACS data. I wanted to look at income as well to see if there was any correlation between areas with higher incomes and places with more cafe coverage. The Census Bureau estimates that the median household income across the US was $74,580 in 2022 (<a href="https://www.census.gov/library/publications/2023/demo/p60-279.html?ref=ifwego.co" rel="noreferrer">source</a>). However, most Bay Area counties have a median household income well over $100,000. If you look at the distributions, the income inequality is pretty stark. The map below shows population density in blue and high-income household density in green. For the purposes of this analysis, I considered households making over $200,000/year to be high income. This is the highest income bucket for which the ACS provides data.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/03/pop-income-bay-area-2-1.png" class="kg-image" alt="Can we identify good locations for a specialty cafe using data?" loading="lazy" width="2000" height="1295" srcset="https://ifwego.co/content/images/size/w600/2024/03/pop-income-bay-area-2-1.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/pop-income-bay-area-2-1.png 1000w, https://ifwego.co/content/images/size/w1600/2024/03/pop-income-bay-area-2-1.png 1600w, https://ifwego.co/content/images/size/w2400/2024/03/pop-income-bay-area-2-1.png 2400w" sizes="(min-width: 1200px) 1200px"><figcaption><span style="white-space: pre-wrap;">Blue is population density, green is high-income household density. You can see pockets where one or the other is more prominent.</span></figcaption></figure><p>You can see some spots of blue or green that don&apos;t overlap much. San Francisco seemed to have a high density of people as well as high-income households while areas up in the hills tended to have a disproportionately high density of high-income households.</p><p>Here&apos;s the same data, but flipping back and forth in a GIF. Blue is population density, green is high-income household density.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/03/pop-v-income-large-slow.gif" class="kg-image" alt="Can we identify good locations for a specialty cafe using data?" loading="lazy" width="2000" height="1295" srcset="https://ifwego.co/content/images/size/w600/2024/03/pop-v-income-large-slow.gif 600w, https://ifwego.co/content/images/size/w1000/2024/03/pop-v-income-large-slow.gif 1000w, https://ifwego.co/content/images/size/w1600/2024/03/pop-v-income-large-slow.gif 1600w, https://ifwego.co/content/images/size/w2400/2024/03/pop-v-income-large-slow.gif 2400w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Blue is population density, green is high-income household density.</span></figcaption></figure><p>Interesting, but what does this mean for cafes? Let&apos;s compare the cafe isochrones to the high-income household density map.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/03/income-isochrone-bay-area.png" class="kg-image" alt="Can we identify good locations for a specialty cafe using data?" loading="lazy" width="2000" height="1295" srcset="https://ifwego.co/content/images/size/w600/2024/03/income-isochrone-bay-area.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/income-isochrone-bay-area.png 1000w, https://ifwego.co/content/images/size/w1600/2024/03/income-isochrone-bay-area.png 1600w, https://ifwego.co/content/images/size/w2400/2024/03/income-isochrone-bay-area.png 2400w" sizes="(min-width: 1200px) 1200px"><figcaption><span style="white-space: pre-wrap;">Green is high-income household density, red is cafe isochrone density.</span></figcaption></figure><p>Eyeballing it, I think this matches up a bit better than against the population graph, but only because large parts of the East Bay are excluded.</p><p>I would caution against drawing strong conclusions from this map. Like I said earlier, the map is heavily influenced by the list of 50 cafes I picked, and small changes to that list (like including a couple more cafes in the East Bay or Tri-Valley) would make these maps look a lot different.</p><h2 id="wanna-try-playing-with-the-map">Wanna Try Playing With The Map?</h2><p>I don&apos;t keep it updated, so consider it an archived version of the map. The list of 50 cafes that I included are not an endorsement. If you want to play around with it, you can find it here (<a href="https://coffee-map.ifwego.co/?ref=ifwego.co">https://coffee-map.ifwego.co/</a>):</p><div class="kg-card kg-button-card kg-align-center"><a href="https://coffee-map.ifwego.co/?ref=ifwego.co" class="kg-btn kg-btn-accent">Go to the Map &#x2197;</a></div><p>A couple pointers:</p><ul><li>The &quot;<code>census</code>&quot;, &quot;<code>zipcodes</code>&quot;, and &quot;<code>places</code>&quot; layers show population density.</li><li>The &quot;<code>censusHighIncome</code>&quot; and &quot;<code>zipcodesHighIncome</code>&quot; layers show high-income household density.</li><li>The &quot;<code>zipcodesIncode</code>&quot; layer shows median income by zip code. It&apos;s the same color as &quot;<code>zipcodesHighIncome</code>&quot; so be sure you have the right one.</li><li>You can click on a census tract/zipcode/place to see stats about it.</li><li>You can enable the &quot;<code>cafePins</code>&quot; layer and click on a pin to see the name of the cafe and its 10 minute isochrone.</li></ul><h2 id="conclusion">Conclusion</h2><p>Maps like these might be useful for seeing where existing cafes are, but they don&apos;t present a clear picture of supply and demand. It might be possible to infer some data about how many coffee drinkers there are in an area based on the density of chain coffee shops like Starbucks and Peet&apos;s that likely have commissioned much more expensive and thorough market research. It&apos;s also probable that a roastery-cafe will start to accumulate their own data on where their customers live around the Bay and could use that data to inform where to put a second cafe.</p><p>Ultimately, a prospective cafe owner has a lot to consider besides the local demand for specialty coffee. People are willing to drive longer than 10 minutes to have good coffee. If some of those other considerations lead to cafes being closer to their communities than to coffee aficionados, I don&apos;t think that&apos;s a bad thing.</p><h2 id="technical-ps-using-duckdb">Technical PS: Using DuckDB</h2><p>I have a friend who works at <a href="https://motherduck.com/?ref=ifwego.co" rel="noreferrer">Mother Duck</a> who won&apos;t stop talking about <a href="https://duckdb.org/?ref=ifwego.co" rel="noreferrer">DuckDB</a>, but I figured since I never shut up about coffee, I could make both of us happy and  use DuckDB for my coffee project.</p><div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text"><b><strong style="white-space: pre-wrap;">What is DuckDB?</strong></b><br><br>Without getting too into the weeds, DuckDB is sorta like Sqlite, a database engine that can run on your computer or be embedded into other applications. More importantly for me, DuckDB is a data-multitool and query engine that lets you grab data from all sorts of sources (not just databases) like CSV and JSON files and run SQL on them.</div></div><p>I had two data files I needed munged together:</p><ol><li>The ACS data CSV tables containing population and income data by census tract and zip code.</li><li>Shape files describing the outline of each zip code and census tract.</li></ol><p>I wanted to get the geometry, population density, and high-income-household density for each census tract and zip code, and output that data to a GeoJSON file that Leaflet could render.</p><p>Here&apos;s a taste of the DuckDB SQL I used to do it:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/03/duckdb-sql.png" class="kg-image" alt="Can we identify good locations for a specialty cafe using data?" loading="lazy" width="2000" height="973" srcset="https://ifwego.co/content/images/size/w600/2024/03/duckdb-sql.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/duckdb-sql.png 1000w, https://ifwego.co/content/images/size/w1600/2024/03/duckdb-sql.png 1600w, https://ifwego.co/content/images/2024/03/duckdb-sql.png 2000w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Census data columns are given column names like &quot;B01003_001E&quot;, and they come with a key that tells you what each column name corresponds to. In this case, &quot;B01003_001E&quot; is the population of the census tract.</span></figcaption></figure><p>Notice how I&apos;m querying data directly from the CSV files using <code>read_csv_auto(...)</code> and writing it out to GeoJSON using <code>WITH (FORMAT GDAL, DRIVER &apos;GeoJSON&apos;)</code>. I thought it was really cool that DuckDB lets you read and write from files as if they&apos;re DB tables.</p><p>In the example above, the <code>census_tracts</code> table already existed. It was created by importing geometry data defining each census tract from a &quot;shape file&quot; I got from the Census Bureau. I imported the data into a table so that I didn&apos;t have to read it in from the file every time I ran a query. I did the same for zip codes.</p><p>Here&apos;s an example query that creates the <code>zip_codes</code> table from its corresponding shape file:</p><pre><code class="language-SQL">/* Import Shapes: Zip Codes */
CREATE TABLE zip_codes AS SELECT 
    ZCTA5CE20,
    GEOID20,
    CLASSFP20,
    MTFCC20,
    FUNCSTAT20,
    ALAND20,
    AWATER20,
    CAST(INTPTLAT20 AS DOUBLE) AS INTPTLAT20,
    CAST(INTPTLON20 AS DOUBLE) AS INTPTLON20,
    ST_GeomFromWKB(wkb_geometry) AS geom
FROM ST_READ(&apos;/Users/dpok/code/coffee-map/data/tl_2021_us_zcta520/tl_2021_us_zcta520.shx&apos;);</code></pre><p>Notice how this query parses the geographic data using <code>ST_GeomFromWKB</code>. The functions for reading and writing geospatial datatypes come from the DuckDB <a href="https://duckdb.org/docs/extensions/spatial.html?ref=ifwego.co" rel="noreferrer">Spatial Extension</a>.</p><p>I had a good time with DuckDB. Some things I enjoyed about using it:</p><ul><li>It&apos;s cool to be able to write a query that joins a DB table with a CSV file.</li><li>There seems to be a lot of support for different file formats like the geospatial shape files and GeoJSON files.</li><li>Considering how much CSV it was consuming and how much JSON it was writing out, it ran pretty quickly. I think it took a few seconds to rebuild the massive JSON blob that I used to power the map.</li></ul>]]></content:encoded></item><item><title><![CDATA[Pattern Making Pt 2: "Making"]]></title><description><![CDATA[<p>In <a href="https://ifwego.co/pattern-making/" rel="noreferrer">Part 1</a>, I mentioned that I made a tool that makes designing patterns easier. It&apos;s very much in alpha, but if you want to check it out, you don&apos;t need to listen to me blab, you can just click the button below (<a href="https://patterns.ifwego.co/?ref=ifwego.co" rel="noreferrer">https://patterns.ifwego.</a></p>]]></description><link>https://ifwego.co/pattern-making-pt-2-making/</link><guid isPermaLink="false">65f36e9b702d0a000145dd49</guid><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><category><![CDATA[Art]]></category><dc:creator><![CDATA[Daniel Pok]]></dc:creator><pubDate>Fri, 15 Mar 2024 18:13:57 GMT</pubDate><media:content url="https://ifwego.co/content/images/2024/03/beasts-16x-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://ifwego.co/content/images/2024/03/beasts-16x-1.png" alt="Pattern Making Pt 2: &quot;Making&quot;"><p>In <a href="https://ifwego.co/pattern-making/" rel="noreferrer">Part 1</a>, I mentioned that I made a tool that makes designing patterns easier. It&apos;s very much in alpha, but if you want to check it out, you don&apos;t need to listen to me blab, you can just click the button below (<a href="https://patterns.ifwego.co/?ref=ifwego.co" rel="noreferrer">https://patterns.ifwego.co</a>).</p><div class="kg-card kg-button-card kg-align-center"><a href="https://patterns.ifwego.co/?ref=ifwego.co" class="kg-btn kg-btn-accent">Try The Tool &#x2197;</a></div><p>I&apos;ll do a quick walkthrough of the tool and then a technical deep dive at the end.</p><h2 id="previewing-patterns">Previewing Patterns</h2><figure class="kg-card kg-image-card"><img src="https://ifwego.co/content/images/2024/03/Screenshot-2024-03-14-at-2.51.09-PM.png" class="kg-image" alt="Pattern Making Pt 2: &quot;Making&quot;" loading="lazy" width="1990" height="1906" srcset="https://ifwego.co/content/images/size/w600/2024/03/Screenshot-2024-03-14-at-2.51.09-PM.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/Screenshot-2024-03-14-at-2.51.09-PM.png 1000w, https://ifwego.co/content/images/size/w1600/2024/03/Screenshot-2024-03-14-at-2.51.09-PM.png 1600w, https://ifwego.co/content/images/2024/03/Screenshot-2024-03-14-at-2.51.09-PM.png 1990w" sizes="(min-width: 720px) 720px"></figure><p>The tool at the top of the page is for previewing patterns. Given a pattern cell, you can preview the cell swapped or tiled in 2x2, 4x4, and 16x16 arrangements. You can get a pattern cell in one of four ways:</p><ol><li><strong>The Drawable Canvas</strong> (<code>&#x270D;&#xFE0F; Draw</code> tab): An in-browser drawing canvas that supports mouse, pen, and touch.</li><li><strong>The Layer Editor</strong> (<code>&#x1F5BC;&#xFE0F; [&#x237A;] Layer Editor</code> tab): An editor for arranging imported images to create a pattern.</li><li><strong>An Image File</strong> (<code>&#x1F4C1; Import File</code> tab): Select an image file from your device.</li><li><strong>The Gallery</strong> (<code>&#x1F5FA;&#xFE0F; Gallery</code> tab): Select from a set of example patterns that illustrate different ways to create patterns.</li></ol><p>Let&apos;s start with...</p><h2 id="the-gallery-link%E2%86%97">The Gallery (<a href="https://patterns.ifwego.co/?ref=ifwego.co#gallery" rel="noreferrer">Link&#x2197;</a>)</h2><p>Here you can find some patterns created in the tool as well as other example and utility patterns.</p><p>All of the following were made using the drawable canvas:</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/caps4x.png" width="1200" height="1200" loading="lazy" alt="Pattern Making Pt 2: &quot;Making&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/caps4x.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/caps4x.png 1000w, https://ifwego.co/content/images/2024/03/caps4x.png 1200w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/eyeguys4x.png" width="600" height="600" loading="lazy" alt="Pattern Making Pt 2: &quot;Making&quot;" srcset="https://ifwego.co/content/images/2024/03/eyeguys4x.png 600w"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/beasts-16x-2.png" width="1200" height="1200" loading="lazy" alt="Pattern Making Pt 2: &quot;Making&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/beasts-16x-2.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/beasts-16x-2.png 1000w, https://ifwego.co/content/images/2024/03/beasts-16x-2.png 1200w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/wires16x.png" width="1200" height="1200" loading="lazy" alt="Pattern Making Pt 2: &quot;Making&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/wires16x.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/wires16x.png 1000w, https://ifwego.co/content/images/2024/03/wires16x.png 1200w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/tubes-16x.png" width="1200" height="1200" loading="lazy" alt="Pattern Making Pt 2: &quot;Making&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/tubes-16x.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/tubes-16x.png 1000w, https://ifwego.co/content/images/2024/03/tubes-16x.png 1200w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/fruit-16x.png" width="1200" height="1200" loading="lazy" alt="Pattern Making Pt 2: &quot;Making&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/fruit-16x.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/fruit-16x.png 1000w, https://ifwego.co/content/images/2024/03/fruit-16x.png 1200w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">There&apos;s more information about each pattern in the gallery.</span></p></figcaption></figure><h2 id="drawable-canvas">Drawable Canvas</h2><p>I made a small drawing tool, originally just as a break from doing math on the other editor (the layer editor), but it ended up being a lot of fun to play with, so I kept fleshing it out until it turned into the state it is today. Here&apos;s a video of me using several of the editing tools to create a tubular pattern.</p><figure class="kg-card kg-embed-card"><iframe width="200" height="150" src="https://www.youtube.com/embed/SC6A0PrXu8k?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen title="Patterns - Drawing On the Canvas"></iframe></figure><p>While the editor is basic in many ways (it has the performance of an online drawing game and evokes MS Paint), it does support a few cool features:</p><ul><li>The main feature: moving around the canvas like it&apos;s an infinite pattern, or swapping the quadrants with a single button press.</li><li>Blurred brushes and opacity, allowing for blending in darker/lighter shades.</li><li>Undo/redo</li><li>Restricting drawing to pen-only (useful for drawing on tablets)</li></ul><p>It&apos;s not the prettiest interface right now (lots of glued-on features), but it&apos;s enough to play with. If you&apos;re unsure what any of the buttons do, I documented them all in the &quot;Instructions&quot; tab (<a href="https://patterns.ifwego.co/?ref=ifwego.co#instructions">https://patterns.ifwego.co/#instructions</a>).</p><p>If you wanna play with it, here are my recommendations:</p><ul><li>You&apos;re not limited to just using the &quot;Swap&quot; button to navigate around the infinite canvas. There are buttons with arrows on them that can pan you around as well. This makes it easier to fill any gaps in your design, not just those around the edges.</li><li>To draw a continuous line that wraps around the edges of the pattern, draw close to the edge, but not all the way, then hit &quot;Swap&quot; and draw in the connection between the two ends. With a bit of blending, you can make it look continuous. Alternatively, if you can draw the line or shape in smaller segments, you can draw a bit, pan the canvas with the arrow buttons, and then keep drawing.</li><li>The editor doesn&apos;t support layers yet, so if you plan to apply shading to an object that will be underneath another one, you may want to add some shading before you start drawing the next element.</li><li>If you want some inspiration, try doodling some simple shapes in different colors. Basic things like stars, diamonds, stick figures and the like can take on a certain charm when tiled.</li></ul><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/sticks-panned-more-16x.png" width="1200" height="1200" loading="lazy" alt="Pattern Making Pt 2: &quot;Making&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/sticks-panned-more-16x.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/sticks-panned-more-16x.png 1000w, https://ifwego.co/content/images/2024/03/sticks-panned-more-16x.png 1200w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/stars-evenmore-64x.png" width="1200" height="1200" loading="lazy" alt="Pattern Making Pt 2: &quot;Making&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/stars-evenmore-64x.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/stars-evenmore-64x.png 1000w, https://ifwego.co/content/images/2024/03/stars-evenmore-64x.png 1200w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Here are two I made with just lines and stars.</span></p></figcaption></figure><h2 id="layer-editor">Layer Editor</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/03/Screenshot-2024-03-14-at-2.22.41-PM.png" class="kg-image" alt="Pattern Making Pt 2: &quot;Making&quot;" loading="lazy" width="1990" height="1964" srcset="https://ifwego.co/content/images/size/w600/2024/03/Screenshot-2024-03-14-at-2.22.41-PM.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/Screenshot-2024-03-14-at-2.22.41-PM.png 1000w, https://ifwego.co/content/images/size/w1600/2024/03/Screenshot-2024-03-14-at-2.22.41-PM.png 1600w, https://ifwego.co/content/images/2024/03/Screenshot-2024-03-14-at-2.22.41-PM.png 1990w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">This is how I made the examples in the previous post, and it&apos;s why I could tweak the elements even after swapping the quadrants around.</span></figcaption></figure><p>This editor is in an earlier stage than the drawable canvas, but I anticipate that this will be the main way I use the tool in the long run. The idea is that you probably want to draw the elements of your pattern in whatever drawing software you&apos;re most familiar with (for me, that&apos;s Procreate on iPad) and then arrange them in this tool to create your final pattern. Like the drawable canvas, the main feature of this tool is previewing how your pattern will look when tiled. If you move an element to the edge of the canvas, it&apos;ll show it wrapping around.</p><figure class="kg-card kg-image-card"><img src="https://ifwego.co/content/images/2024/03/Screenshot-2024-03-14-at-3.35.55-PM.png" class="kg-image" alt="Pattern Making Pt 2: &quot;Making&quot;" loading="lazy" width="1366" height="1328" srcset="https://ifwego.co/content/images/size/w600/2024/03/Screenshot-2024-03-14-at-3.35.55-PM.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/Screenshot-2024-03-14-at-3.35.55-PM.png 1000w, https://ifwego.co/content/images/2024/03/Screenshot-2024-03-14-at-3.35.55-PM.png 1366w" sizes="(min-width: 720px) 720px"></figure><p>You can also hit the &quot;Swap Quadrants&quot; button to move the elements around. This works at the object level, not the pixel level, so you can continue moving, scaling, and rotating each element after you hit &quot;Swap&quot;. That feature alone was my greatest motivation to build the tool. Every time I edited a pattern in Procreate or Affinity Photo I wished they would let me edit this way.</p><p>Here&apos;s a pattern I made using elements drawn in Procreate and arranged using the layer editor tool, all on my iPad:</p><figure class="kg-card kg-image-card"><img src="https://ifwego.co/content/images/2024/03/snaggletooth-16x-1.png" class="kg-image" alt="Pattern Making Pt 2: &quot;Making&quot;" loading="lazy" width="1200" height="1200" srcset="https://ifwego.co/content/images/size/w600/2024/03/snaggletooth-16x-1.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/snaggletooth-16x-1.png 1000w, https://ifwego.co/content/images/2024/03/snaggletooth-16x-1.png 1200w" sizes="(min-width: 720px) 720px"></figure><p>I haven&apos;t written up instructions for how to use this tool like I did for the drawable canvas, but here are some key pointers:</p><ul><li>The canvas starts with a red square. Feel free to play with it to get a feel for the manipulation handles.</li><li>You can delete an element by pressing the <code>Delete</code> key on your keyboard, or by selecting an item and then pressing the &quot;Delete&quot; button in the UI.</li><li>You can scale an item symmetrically by holding <code>Alt</code>/<code>Option</code> while dragging a resize handle.</li><li>You can add colored frames by checking the &quot;Include Background&quot; checkbox and clicking &quot;Add Frame&quot;. You can also include a background color when you add an image.</li></ul><div class="kg-card kg-callout-card kg-callout-card-yellow"><div class="kg-callout-emoji">&#x26A0;&#xFE0F;</div><div class="kg-callout-text"><b><strong style="white-space: pre-wrap;">Quick callout</strong></b>: to preview the pattern you&apos;re making in this editor in the pattern preview canvas at the top of the screen, you have to hit the &quot;Update Preview Image&quot; button each time you want to refresh the preview. <br><br>You also can&apos;t right-click &quot;Save As&quot; the contents of the editor. You have to click the &quot;Download&quot; button or right-click the image in the &quot;Image Preview&quot; dropdown to save it as an image.<br><br><code spellcheck="false" style="white-space: pre-wrap;">&lt;&#x1F913;&gt; This is because the layer editor uses HTML elements and CSS transforms to render the individual images intead of drawing directly to a &lt;canvas /&gt; element like the drawable canvas and pattern preview do. &lt;/&#x1F913;&gt;</code></div></div><p>That&apos;s all I&apos;ve got for usage notes. Feel free to play with it if you&apos;d like! </p><p>If you don&apos;t wanna hear about my technical kvetching, then I&apos;ll say goodbye to you here, and leave another link to the tool (<a href="https://patterns.ifwego.co/?ref=ifwego.co" rel="noreferrer">https://patterns.ifwego.co</a>):</p><div class="kg-card kg-button-card kg-align-center"><a href="https://patterns.ifwego.co/?ref=ifwego.co" class="kg-btn kg-btn-accent">Try The Tool &#x2197;</a></div><p>On with the kvetching!</p><h2 id="my-mortal-enemy-shapes-and-colors">My Mortal Enemy: Shapes and Colors</h2><p>I&apos;ve never loved geometry. I still remember taking on a photo-rotating task as an intern and handing it back after a day of stumped whiteboarding trying to figure out why my trig was a little off. (...the debugger was also broken and the app took so long to build that you could take a walk while it was building, soooo.....)</p><p>Making the resize and rotate handles on the layer editor took a day or two of experimenting to get right.</p><p>The first and easiest operation was translation (that is, moving the box around the canvas). This is done with a CSS <code>transform: translate(...)</code> property and some pointer event handlers. Next up was scaling. Scaling is a bit more nuanced because the behavior users expect from a resize handle is a little more complicated than doing a <code>scale(X, Y, Z)</code> transform.</p><ul><li>If the user drags on the right-side handle, it should increase the width of the box while keeping the height constant and the left edge of the box constant.</li><li>If the user drags on the left-side handle though, the origin of the box needs to move to the left to compensate for the increasing width because the user expects the box to be anchored on the right edge.</li><li>If the user drags on one of the corner handles, they expect the box to scale proportionally.</li><li>In all cases, the box should scale such that the anchor the user is dragging stays under their pointer if possible &#x2013; the pointer could move in the wrong axis or a proportional scaling constraint could prevent the box from reaching the user&apos;s pointer.</li><li>If the user holds down <code>Alt</code>/<code>Option</code>, then the box should scale symmetrically (e.g. twice as much, but also translate negative the amount).</li><li>If the user holds down <code>Shift</code> when dragging a side handle (instead of the corner ones), the box should scale proportionally.</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/03/Screenshot-2024-03-14-at-4.12.12-PM.png" class="kg-image" alt="Pattern Making Pt 2: &quot;Making&quot;" loading="lazy" width="2000" height="970" srcset="https://ifwego.co/content/images/size/w600/2024/03/Screenshot-2024-03-14-at-4.12.12-PM.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/Screenshot-2024-03-14-at-4.12.12-PM.png 1000w, https://ifwego.co/content/images/size/w1600/2024/03/Screenshot-2024-03-14-at-4.12.12-PM.png 1600w, https://ifwego.co/content/images/2024/03/Screenshot-2024-03-14-at-4.12.12-PM.png 2000w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Examples of scaling to/from anchors. The diagonal vectors aren&apos;t normalized, but I didn&apos;t want to put </span><code spellcheck="false" style="white-space: pre-wrap;"><span>sqrt(2)</span></code><span style="white-space: pre-wrap;"> everywhere.</span></figcaption></figure><p>This isn&apos;t too hard to do, but it does require managing a few vectors:</p><ul><li>The pointer offset from its starting location.</li><li>The &quot;drag axis&quot; of the handle (e.g. the vector representing the positive direction)</li><li>The &quot;scale axis&quot; of the handle (e.g. the amount to scale the box based on how far the user dragged in the drag axis).</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/03/20240204-16-25-36.png" class="kg-image" alt="Pattern Making Pt 2: &quot;Making&quot;" loading="lazy" width="1112" height="861" srcset="https://ifwego.co/content/images/size/w600/2024/03/20240204-16-25-36.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/20240204-16-25-36.png 1000w, https://ifwego.co/content/images/2024/03/20240204-16-25-36.png 1112w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Green: Pointer Vector, Red: Drag Vector (unit vector), Blue: Scale Vector (unit vector)</span></figcaption></figure><p>Math time:</p><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x2797;</div><div class="kg-callout-text">1. The magnitude of the drag is the pointer offset projected onto the drag vector. (<code spellcheck="false" style="white-space: pre-wrap;">p&#xB7;d</code>, or <code spellcheck="false" style="white-space: pre-wrap;">p_x * d_x + p_y * d_y</code>).<br><br>2. Multiply by the scale vector to get the delta in width and height.<br><br>3. If the drag vector has a negative component, then translate the box back by the same amount as the width or height increased to keep the opposite edge anchored.</div></div><p>So the right, bottom, and bottom-right anchors only have positive drag vectors and don&apos;t require a compensating translation (CSS coordinates, like in most graphics systems, have the origin in the top left of the screen or bounding box, and increase in the <code>y</code> direction from top to bottom). All the other anchors require subtracting a delta in width or height when scaling. This works in reverse too: if you&apos;re shrinking the box using the left anchor (e.g. by dragging to the right), you&apos;ll need to subtract the negative delta width which turns into a positive X translation to ensure that the right edge remains fixed.</p><p>Ok, scaling boxes makes sense. Time to enable rotation. CSS transforms can do it using: <code>transform: rotate(...)</code>, so it should be easy, right? All you gotta do is find the pivot you&apos;re rotating around (by default in CSS, it&apos;s the center of the object) and use the inverse trig functions to find the delta in angle when you drag the rotate handle around.</p><p>I tried it, and after a bit of work to use the right inverse trig functions, rotation was working. Nice! Then I tried scaling after rotating and the boxes would translate around when scaling if they were rotated. Wups. What&apos;s up with that?</p><p>Before adding rotations, we kept the opposite edge (or point) from the scale anchor fixed in space by compensating for changes in width or height by translating the X and Y. Now with rotation, that&apos;s insufficient. When the box is scaled, its center of rotation changes even if its <code>(x, y)</code> position (its top-left corner, before rotating) remains the same. However, to keep the opposing edge fixed, we need to keep the box&apos;s apparent origin fixed, accounting for where its center was at the start of the scaling operation.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/Screenshot-2024-03-14-at-4.54.15-PM.png" width="1188" height="742" loading="lazy" alt="Pattern Making Pt 2: &quot;Making&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/Screenshot-2024-03-14-at-4.54.15-PM.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/Screenshot-2024-03-14-at-4.54.15-PM.png 1000w, https://ifwego.co/content/images/2024/03/Screenshot-2024-03-14-at-4.54.15-PM.png 1188w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/Screenshot-2024-03-14-at-4.58.32-PM.png" width="356" height="356" loading="lazy" alt="Pattern Making Pt 2: &quot;Making&quot;"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/Screenshot-2024-03-14-at-6.02.01-PM.png" width="1054" height="688" loading="lazy" alt="Pattern Making Pt 2: &quot;Making&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/Screenshot-2024-03-14-at-6.02.01-PM.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/Screenshot-2024-03-14-at-6.02.01-PM.png 1000w, https://ifwego.co/content/images/2024/03/Screenshot-2024-03-14-at-6.02.01-PM.png 1054w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">1st: Desired scaling behavior. 2nd: A square at the origin gets rotated about its center such that the top-left corner of its bounding box is above the origin. 3rd: As width increases, the center moves rightwards (</span><code spellcheck="false" style="white-space: pre-wrap;"><span>+X</span></code><span style="white-space: pre-wrap;">), causing an apparent translation.</span></p></figcaption></figure><p>I spent a while trying to derive how to calculate the delta X and Y directly from the delta width and height (given a fixed rotation), but found that you need the original X and Y to find the top-left point of its bounding box.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/03/Screenshot-2024-03-14-at-5.00.02-PM.png" class="kg-image" alt="Pattern Making Pt 2: &quot;Making&quot;" loading="lazy" width="1448" height="1312" srcset="https://ifwego.co/content/images/size/w600/2024/03/Screenshot-2024-03-14-at-5.00.02-PM.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/Screenshot-2024-03-14-at-5.00.02-PM.png 1000w, https://ifwego.co/content/images/2024/03/Screenshot-2024-03-14-at-5.00.02-PM.png 1448w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Lots more chicken scratch. I&apos;m pretty sure this isn&apos;t quite right anyhow, so I won&apos;t include the rest of the diagrams.</span></figcaption></figure><p>To address this I did two things:</p><ol><li>Store each object&apos;s position as a vector with two halves: a set of &quot;committed&quot; values for X, Y, width, height, and rotation, and a set of &quot;delta&quot; values for each of those. When the user finished scaling or rotating the object, the deltas would get combined into the committed values. This way, I could ensure that transforms would be correct relative to the original position.</li><li>I calculated the <code>dX</code> and <code>dY</code> by applying the simple algorithm for unrotated boxes to get a rotation in &quot;box space&quot;, rotating it about its new center, finding the new bounding box, and then finding the delta needed to get the top-left of the bounding box back to the top-left of the object&apos;s bounding box before we started scaling.</li></ol><h2 id="other-learnings">Other Learnings</h2><p>Enough with the shapes. Here are a handful of other things I encountered while building out this prototype:</p><ul><li>Use <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/setPointerCapture?ref=ifwego.co" rel="noreferrer"><code>element.setPointerCapture</code></a><code>(event.pointerId)</code> in the <code>pointerDown</code> handler to ensure that all subsequent pointer events get sent to a certain element (even if the pointer leaves the element&apos;s bounds). This allows you to continue drawing even if the pointer leaves the canvas, or to keep dragging resize handles even if the pointer doesn&apos;t stay 1:1 on top of the handle element. When the pointer is captured, it can still fire <code>onPointerLeave</code> events if it&apos;s no longer over the element.</li><li>To prevent extra button latency on mobile, set the CSS property <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action?ref=ifwego.co" rel="noreferrer"><code>touch-action</code></a> to <code>manipulation</code> (or an even more restrictive value). This prevents the double-tap to zoom behavior in mobile browsers, which means they can fire touch events without waiting to see if it&apos;s a double tap. Avoid hover states on mobile as well: elements with hover states can eat touch actions going into their hover state. There&apos;s a media query for this: <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/hover?ref=ifwego.co" rel="noreferrer"><code>@media (hover: hover)</code></a>.</li><li>You can detect light mode/dark mode with the media query <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme?ref=ifwego.co" rel="noreferrer"><code>@media (prefers-color-scheme: light)</code></a></li><li>When users do long touches on the screen (such as when drawing on the canvas or doing a drag), mobile browsers may interpret that as a select and try to select the nearest text. To prevent this, use the CSS property &quot;<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/user-select?ref=ifwego.co" rel="noreferrer"><code>user-select: none</code></a>&quot; and the prefixed &quot;<code>-webkit-user-select: none</code>&quot; (<code>WebkitUserSelect</code> in JS). Be careful though, this prevents users from selecting whatever text this property is applied to, which could make it hard for them to copy/paste or look up some text that they ought to be able to. I&apos;m still tweaking my approach to this, but for now, I apply this property as high up in the DOM as possible when the user is drawing, and then remove it when they stop. I also marked the control buttons as non-selectable at all times. This sorta works, but it still selects text way down the page sometimes when I&apos;m drawing on my iPad.</li><li>To implement editor history for the drawable canvas, I store an array containing the previous contents of the canvas. Originally, I tried using <a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/getImageData?ref=ifwego.co" rel="noreferrer"><code>canvasCtx.getImageData()</code></a> and storing the resulting array because it&apos;s the most native way to get and put image data to the canvas. However, this array is not compressed, and my canvas data is readily compressible since the brushes that users use are pretty wide and create large patches of the same color (which the PNG format should be able to exploit). I switched from <code>canvasCtx.getImageData()</code> to <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL?ref=ifwego.co" rel="noreferrer"><code>canvasElem.toDataUrl()</code></a> which returns the contents of the canvas as a base64 encoded PNG. This saved a lot of space and allowed me to increase the number of saved history states to 100.</li><li>Safari doesn&apos;t support blurs in canvas. Most other browsers do, but also all browsers on iOS are Safari/Webkit under the hood (at least for now), so iOS users are out of luck for blurs. I implemented a simulated blur using multiple strokes roughly scaled to match a Gaussian function, but I haven&apos;t tuned it correctly yet.</li><li>I have an issue when drawing on my iPad where there&apos;s a minimum delay between strokes. It will straight up drop every other stroke if I go too fast. I&apos;m unsure if this is a browser issue that limits the maximum speed I can detect touches (I doubt it because it feels like I can tap buttons faster), or if my rendering code is too slow on each <code>pointerUp</code> event and blocking the UI thread (I did some profiling and tried disabling costly operations like pushing entries to the back stack, but it didn&apos;t seem to make a difference).</li><li>The checkerboard pattern backgrounds for the various canvases and editors are pure CSS. They&apos;re a repeating CSS conical gradient with some transparency. I figured this would be most efficient way to do it since the browser can quickly generate the texture using whatever native GPU magic it has and then repeat it to match the screen size. The magic incantation is this:</li></ul><pre><code class="language-CSS">.checker {
  background-image: conic-gradient(
    #ddd3 0 0.25turn,
    #3333 0.25turn 0.5turn,
    #ddd3 0.5turn 0.75turn,
    #3333 0.75turn 1turn
  );
  background-size: 50px 50px;
}
</code></pre><h2 id="next-steps">Next Steps</h2><p>To be honest, I&apos;m not sure what I&apos;ll do next with this project. It&apos;s working well enough for doodling patterns with the drawable canvas, and the ability to pan around the infinite canvas while doodling has changed the way I approach pattern making. Namely, it&apos;s letting me play with directly illustrated patterns like &quot;Beasts&quot; and the variations on wires/tubes/pipes instead of just using multi-element patterns built from separately-drawn pieces. The layer editor is still rough and it could use some of the polish I added to the drawable canvas like undo/redo and the ability to pan around the infinite canvas instead of just doing a quadrant swap.</p><p>I&apos;ve debated combing the two editors into a web-based drawing/layout tool or maybe reusing the drawable canvas for some browser games. It could also be a fun opportunity to learn WebGL or use WASM to further optimize some drawing and compositing operations. I&apos;ve also seen some cool patterns that never repeat, and I think it could be fun to try to make a pattern generator that&apos;s compatible with them (e.g. the<a href="https://news.ycombinator.com/item?id=33818693&amp;ref=ifwego.co" rel="noreferrer"> Penrose Tiling</a>), but I need a break from geometry for a while.</p><p>If you end up making something with the tool or have feedback on it, let me know!</p>]]></content:encoded></item><item><title><![CDATA[Pattern Making Pt 1: "Patterns"]]></title><description><![CDATA[<p>Late last year, I designed some packaging for a collection of coffee samples I gave out to some friends. I&apos;m not much of an artist. The best I can manage are simple illustrations of geometric things, so I used a trick I learned in the one graphic design</p>]]></description><link>https://ifwego.co/pattern-making/</link><guid isPermaLink="false">65f2909f702d0a000145dc0e</guid><category><![CDATA[Projects]]></category><category><![CDATA[Art]]></category><dc:creator><![CDATA[Daniel Pok]]></dc:creator><pubDate>Thu, 14 Mar 2024 19:51:27 GMT</pubDate><media:content url="https://ifwego.co/content/images/2024/03/front-cropped.png" medium="image"/><content:encoded><![CDATA[<img src="https://ifwego.co/content/images/2024/03/front-cropped.png" alt="Pattern Making Pt 1: &quot;Patterns&quot;"><p>Late last year, I designed some packaging for a collection of coffee samples I gave out to some friends. I&apos;m not much of an artist. The best I can manage are simple illustrations of geometric things, so I used a trick I learned in the one graphic design class I took in college to stretch my limited drawing abilities into a useful art asset: turning the simple illustrations into a repeating pattern.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/03/purp-badge-scribble-noblob.png" class="kg-image" alt="Pattern Making Pt 1: &quot;Patterns&quot;" loading="lazy" width="2000" height="1303" srcset="https://ifwego.co/content/images/size/w600/2024/03/purp-badge-scribble-noblob.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/purp-badge-scribble-noblob.png 1000w, https://ifwego.co/content/images/size/w1600/2024/03/purp-badge-scribble-noblob.png 1600w, https://ifwego.co/content/images/2024/03/purp-badge-scribble-noblob.png 2000w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Here&apos;s a draft of the bag, showing the pattern across the front and back.</span></figcaption></figure><p>It&apos;s an approximation of a fully illustrated background image. Take, for example, the light-mode WhatsApp background: it doesn&apos;t repeat, even though it goes off the edge. I love the way it looks. The hand-drawn, line-art style reminds me of middle-school notebooks where every available space has been filled in with doodles. They take a lot of creativity and time to create &#x2013; more than I have.</p><p>Have a look for yourself:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/03/IMG_3382.PNG" class="kg-image" alt="Pattern Making Pt 1: &quot;Patterns&quot;" loading="lazy" width="1284" height="2778" srcset="https://ifwego.co/content/images/size/w600/2024/03/IMG_3382.PNG 600w, https://ifwego.co/content/images/size/w1000/2024/03/IMG_3382.PNG 1000w, https://ifwego.co/content/images/2024/03/IMG_3382.PNG 1284w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">I count over 60 unique elements. I see some repeated elements like the &#x1F60D; and &#x1F4F7;, but I don&apos;t see any places where the entire pattern repeats. </span></figcaption></figure><p>So the value of the pattern making trick is to stretch a pattern cell into a larger asset, without making the tiling obvious. This lets us emulate a full-fat illustration while drawing fewer elements. Here&apos;s the pattern on my coffee bag:</p><figure class="kg-card kg-image-card"><img src="https://ifwego.co/content/images/2024/03/tiled-preview-clear.png" class="kg-image" alt="Pattern Making Pt 1: &quot;Patterns&quot;" loading="lazy" width="2000" height="1545" srcset="https://ifwego.co/content/images/size/w600/2024/03/tiled-preview-clear.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/tiled-preview-clear.png 1000w, https://ifwego.co/content/images/size/w1600/2024/03/tiled-preview-clear.png 1600w, https://ifwego.co/content/images/size/w2400/2024/03/tiled-preview-clear.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>It&apos;s not hard to spot where the pattern repeats, but I think it passes the sniff test at first glance (especially if it&apos;s incorporated into a bigger design, like on the bags). The key is to break up the outline of the pattern cell. Humans are very perceptive to outlines, even if they&apos;re implied by white space (see also: all of modern web design that uses white space instead of actual lines). The pattern cell is a square, but if the art fit neatly inside that square, then the whitespace around the edges would strongly imply a tiled grid, which would make the boundaries of the pattern cell really obvious.</p><p>Here&apos;s the pattern cell for the pattern above:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/03/pattern.png" class="kg-image" alt="Pattern Making Pt 1: &quot;Patterns&quot;" loading="lazy" width="900" height="900" srcset="https://ifwego.co/content/images/size/w600/2024/03/pattern.png 600w, https://ifwego.co/content/images/2024/03/pattern.png 900w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">The elements of the pattern were inspired by some of my favorite coffee gear (...or at least the more aesthetically shaped ones).</span></figcaption></figure><p>See the trick? The elements go off the edge and &quot;wrap around&quot;, which breaks up the outline. It might not look like it, but it uses the same technique as camouflage: by breaking up the outline of the square pattern cell, we make it harder to pick it out.</p><p>Here&apos;s the pattern again, repeated in a 2x2 grid with each set of elements highlighted:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/03/coffee-pattern-4x-highlighted.png" class="kg-image" alt="Pattern Making Pt 1: &quot;Patterns&quot;" loading="lazy" width="1200" height="1200" srcset="https://ifwego.co/content/images/size/w600/2024/03/coffee-pattern-4x-highlighted.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/coffee-pattern-4x-highlighted.png 1000w, https://ifwego.co/content/images/2024/03/coffee-pattern-4x-highlighted.png 1200w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Notice how elements going off the bottom wrap around the to the top, and ones going off the right come back around the left. That&apos;s gonna come up again in a second. There are four pattern cells in this 2x2 grid, so there are 4 groups of elements.</span></figcaption></figure><p>Let&apos;s try making a new pattern out of these elements. There are around 20 elements in the pattern. Here&apos;s eleven of them arranged in a pattern cell:</p><figure class="kg-card kg-image-card"><img src="https://ifwego.co/content/images/2024/03/coffee-remake-1-1.png" class="kg-image" alt="Pattern Making Pt 1: &quot;Patterns&quot;" loading="lazy" width="1200" height="1200" srcset="https://ifwego.co/content/images/size/w600/2024/03/coffee-remake-1-1.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/coffee-remake-1-1.png 1000w, https://ifwego.co/content/images/2024/03/coffee-remake-1-1.png 1200w" sizes="(min-width: 720px) 720px"></figure><p>And now let&apos;s try that in a 2x2 pattern:</p><figure class="kg-card kg-image-card"><img src="https://ifwego.co/content/images/2024/03/coffee-remake-1-4x.png" class="kg-image" alt="Pattern Making Pt 1: &quot;Patterns&quot;" loading="lazy" width="1200" height="1200" srcset="https://ifwego.co/content/images/size/w600/2024/03/coffee-remake-1-4x.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/coffee-remake-1-4x.png 1000w, https://ifwego.co/content/images/2024/03/coffee-remake-1-4x.png 1200w" sizes="(min-width: 720px) 720px"></figure><p>See that giant cross of white space going through the center? Even though the elements go close to the edge of the pattern cell, they never cross it, and that&apos;s enough for your brain to see the lines of white space.</p><p>Ok, &quot;one neat trick&quot; time: the way to easily add elements that &quot;wrap around&quot; is to swap the quadrants of the image such that the center is now on the sides, and then add more elements to the center. In practical terms, that means swapping the top-left quadrant with the bottom-right, and the top-right with the bottom-left. Is that hard to visualize? Let&apos;s look at some examples.</p><p>Here&apos;s the pattern cell from above, but after doing the swap I described. The cross of blank space in the center is from the blank space that used to be around the edges.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/03/coffee-remake-1-swapped.png" class="kg-image" alt="Pattern Making Pt 1: &quot;Patterns&quot;" loading="lazy" width="1200" height="1200" srcset="https://ifwego.co/content/images/size/w600/2024/03/coffee-remake-1-swapped.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/coffee-remake-1-swapped.png 1000w, https://ifwego.co/content/images/2024/03/coffee-remake-1-swapped.png 1200w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">The pattern cell from above, but swapped.</span></figcaption></figure><p>To see exactly what the swap does, I made this pattern cell with labeled quadrants. Note how after the swap, the &quot;circle&quot; in the center is now spread out around all four corners, and the angles in the corners are now in the center. In effect, doing this &quot;swap&quot; is just panning your infinite pattern from the &quot;middle&quot; of a pattern cell to the corner where four cells meet.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/quadrants.png" width="1200" height="1200" loading="lazy" alt="Pattern Making Pt 1: &quot;Patterns&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/quadrants.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/quadrants.png 1000w, https://ifwego.co/content/images/2024/03/quadrants.png 1200w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/quadrants-swapped.png" width="1200" height="1200" loading="lazy" alt="Pattern Making Pt 1: &quot;Patterns&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/quadrants-swapped.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/quadrants-swapped.png 1000w, https://ifwego.co/content/images/2024/03/quadrants-swapped.png 1200w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/quadrants-4x.png" width="1200" height="1200" loading="lazy" alt="Pattern Making Pt 1: &quot;Patterns&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/quadrants-4x.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/quadrants-4x.png 1000w, https://ifwego.co/content/images/2024/03/quadrants-4x.png 1200w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">1st: Original pattern cell. 2nd: Pattern cell after swapping. 3rd: Pattern in a 2x2 grid. Notice how the &quot;swapped&quot; cell is the same as centering on the point where the four cells meet.</span></p></figcaption></figure><p>Ok, so let&apos;s try out the trick. We&apos;ll start out with the &quot;swapped&quot; pattern cell and then add some more elements to fill the blank space:</p><figure class="kg-card kg-image-card"><img src="https://ifwego.co/content/images/2024/03/coffee-remake-2-1.png" class="kg-image" alt="Pattern Making Pt 1: &quot;Patterns&quot;" loading="lazy" width="1200" height="1200" srcset="https://ifwego.co/content/images/size/w600/2024/03/coffee-remake-2-1.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/coffee-remake-2-1.png 1000w, https://ifwego.co/content/images/2024/03/coffee-remake-2-1.png 1200w" sizes="(min-width: 720px) 720px"></figure><p>Let&apos;s see how that looks in a 2x2 grid:</p><figure class="kg-card kg-image-card"><img src="https://ifwego.co/content/images/2024/03/coffee-remake-2-4x-1.png" class="kg-image" alt="Pattern Making Pt 1: &quot;Patterns&quot;" loading="lazy" width="1200" height="1200" srcset="https://ifwego.co/content/images/size/w600/2024/03/coffee-remake-2-4x-1.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/coffee-remake-2-4x-1.png 1000w, https://ifwego.co/content/images/2024/03/coffee-remake-2-4x-1.png 1200w" sizes="(min-width: 720px) 720px"></figure><p>It&apos;s definitely better. The blank cross is gone, but there are still some awkward blank spaces near the portafilter (that&apos;s the &#x1F50E; looking thing that goes in the espresso machine). It would help even more if we adjusted the positions and rotations of the existing elements to hide the seams a bit better, but that can be hard to do if you did the swap on the pixels of the image and no longer have individual element objects to manipulate.</p><p>Good thing I didn&apos;t do this in a normal image editing tool (foreshadowing much?).</p><p>Let&apos;s tweak it a bit:</p><figure class="kg-card kg-image-card"><img src="https://ifwego.co/content/images/2024/03/coffee-remake-3.png" class="kg-image" alt="Pattern Making Pt 1: &quot;Patterns&quot;" loading="lazy" width="1200" height="1200" srcset="https://ifwego.co/content/images/size/w600/2024/03/coffee-remake-3.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/coffee-remake-3.png 1000w, https://ifwego.co/content/images/2024/03/coffee-remake-3.png 1200w" sizes="(min-width: 720px) 720px"></figure><p>And in a 2x2 grid:</p><figure class="kg-card kg-image-card"><img src="https://ifwego.co/content/images/2024/03/coffee-remake-3-4x.png" class="kg-image" alt="Pattern Making Pt 1: &quot;Patterns&quot;" loading="lazy" width="1200" height="1200" srcset="https://ifwego.co/content/images/size/w600/2024/03/coffee-remake-3-4x.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/coffee-remake-3-4x.png 1000w, https://ifwego.co/content/images/2024/03/coffee-remake-3-4x.png 1200w" sizes="(min-width: 720px) 720px"></figure><p>It&apos;s not perfect, but it&apos;s better. Let&apos;s take a quick look back:</p><ol><li>We arranged the elements in a cell, trying to get close to the edges, but it the seams of blank space still stuck out after tiling it.</li><li>We &quot;swapped&quot; the quadrants of the pattern cell and added some more elements in the blank space to break up the outline of the pattern cell. This produced a pattern with much more subtle seams, but there was still some awkward blank space.</li><li>We adjusted the elements a bit to hide the blank spaces and smooth out the visual pacing.</li></ol><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/coffee-remake-1-4x-1.png" width="1200" height="1200" loading="lazy" alt="Pattern Making Pt 1: &quot;Patterns&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/coffee-remake-1-4x-1.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/coffee-remake-1-4x-1.png 1000w, https://ifwego.co/content/images/2024/03/coffee-remake-1-4x-1.png 1200w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/coffee-remake-2-4x-2.png" width="1200" height="1200" loading="lazy" alt="Pattern Making Pt 1: &quot;Patterns&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/coffee-remake-2-4x-2.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/coffee-remake-2-4x-2.png 1000w, https://ifwego.co/content/images/2024/03/coffee-remake-2-4x-2.png 1200w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/coffee-remake-3-4x-1.png" width="1200" height="1200" loading="lazy" alt="Pattern Making Pt 1: &quot;Patterns&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/coffee-remake-3-4x-1.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/coffee-remake-3-4x-1.png 1000w, https://ifwego.co/content/images/2024/03/coffee-remake-3-4x-1.png 1200w" sizes="(min-width: 720px) 720px"></div></div></div></figure><p>That&apos;s the gist of it. I&apos;m sure real artists could share some deeper knowledge about how to make visually pleasing patterns, but here are some more thoughts I had about them:</p><ul><li><strong>These multi-element patterns need to start with good, consistent elements.</strong> I drew the individual elements using <a href="https://apps.apple.com/us/app/procreate/id425073498?ref=ifwego.co" rel="noreferrer">Procreate</a> on my iPad using the Apple Pencil. Procreate makes it easy to achieve that hand-drawn style, but maintaining consistency between elements is up to you. Notice how some of my elements are empty and figurative while others have more detail and implied shading. Ideally, they would all be similar instead.</li><li><strong>Keep most elements similarly sized so that things don&apos;t stand out.</strong> When one element is a lot larger than the others, it creates a visual anchor that makes the repeating more obvious. It&apos;s nice to have a mix of sizes, but be careful when making a small handful of items a lot bigger than the others. Notice how the WhatsApp pattern uses small dots and stars as decorations, but most of the important elements are about the same size. That illustration can get away with having a few large &quot;hero&quot; elements because it doesn&apos;t actually repeat. The hero elements stand out, but they&apos;re unique. Similarly, watch out for elements that are darker or more colorful than others standing out too much.</li><li><strong>Avoid prominent parallel lines.</strong> In addition to whitespace, humans are good at spotting parallel lines. Any prominent straight line in a pattern becomes a parallel one and creates implicit outlines. Try to rotate things slightly in different directions to avoid having all straight lines in the pattern be parallel. On the other hand, leaning into the parallel lines can create some cool effects. You don&apos;t have to hide the pattern if you don&apos;t want to.</li></ul><p>I&apos;ll leave you with a few of the patterns I&apos;ve made and how I&apos;ve used them:</p><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/Untitled_Artwork-8.png" width="2000" height="2000" loading="lazy" alt="Pattern Making Pt 1: &quot;Patterns&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/Untitled_Artwork-8.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/Untitled_Artwork-8.png 1000w, https://ifwego.co/content/images/size/w1600/2024/03/Untitled_Artwork-8.png 1600w, https://ifwego.co/content/images/2024/03/Untitled_Artwork-8.png 2048w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/Untitled_Artwork-9.png" width="2000" height="3091" loading="lazy" alt="Pattern Making Pt 1: &quot;Patterns&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/Untitled_Artwork-9.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/Untitled_Artwork-9.png 1000w, https://ifwego.co/content/images/size/w1600/2024/03/Untitled_Artwork-9.png 1600w, https://ifwego.co/content/images/size/w2400/2024/03/Untitled_Artwork-9.png 2400w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/Untitled_Artwork-7-1.png" width="2000" height="3091" loading="lazy" alt="Pattern Making Pt 1: &quot;Patterns&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/Untitled_Artwork-7-1.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/Untitled_Artwork-7-1.png 1000w, https://ifwego.co/content/images/size/w1600/2024/03/Untitled_Artwork-7-1.png 1600w, https://ifwego.co/content/images/size/w2400/2024/03/Untitled_Artwork-7-1.png 2400w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/03/DH-Coaster-no-qr-1.png" width="1200" height="1200" loading="lazy" alt="Pattern Making Pt 1: &quot;Patterns&quot;" srcset="https://ifwego.co/content/images/size/w600/2024/03/DH-Coaster-no-qr-1.png 600w, https://ifwego.co/content/images/size/w1000/2024/03/DH-Coaster-no-qr-1.png 1000w, https://ifwego.co/content/images/2024/03/DH-Coaster-no-qr-1.png 1200w" sizes="(min-width: 720px) 720px"></div></div></div></figure><p>Doing the quadrant swap manually in Procreate and other image editing tools is a pain and makes it hard to adjust things afterwards. I&apos;ve wanted to make myself a better tool for this, and I finally got around to it.</p><p>In <a href="https://ifwego.co/pattern-making-pt-2-making/" rel="noreferrer">Part 2</a>, I&apos;ll talk about that tool, how to use it, and maybe a bit about how I built it.</p><div class="kg-card kg-button-card kg-align-left"><a href="https://ifwego.co/pattern-making-pt-2-making/" class="kg-btn kg-btn-accent">Onwards to Part 2&#x2197;</a></div>]]></content:encoded></item><item><title><![CDATA[Have B-Track Work]]></title><description><![CDATA[<p>One of my best technical mentors, <a href="https://www.linkedin.com/in/cameron-behar?ref=ifwego.co" rel="noreferrer">Cameron Behar</a> (CTO of <a href="https://www.sprinterhealth.com/about?ref=ifwego.co" rel="noreferrer">Sprinter Health</a>), gave me this advice for working more efficiently when your project gets blocked or iteration times are slow: <strong>Have a secondary workstream that you can switch to when your primary one is blocked.</strong></p><p>Getting blocked on work causes</p>]]></description><link>https://ifwego.co/have-b-track-work/</link><guid isPermaLink="false">657255a27f8c5000015f03e8</guid><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Daniel Pok]]></dc:creator><pubDate>Thu, 22 Feb 2024 00:11:11 GMT</pubDate><media:content url="https://ifwego.co/content/images/2023/12/DSCF9340-copy-1.jpeg" medium="image"/><content:encoded><![CDATA[<img src="https://ifwego.co/content/images/2023/12/DSCF9340-copy-1.jpeg" alt="Have B-Track Work"><p>One of my best technical mentors, <a href="https://www.linkedin.com/in/cameron-behar?ref=ifwego.co" rel="noreferrer">Cameron Behar</a> (CTO of <a href="https://www.sprinterhealth.com/about?ref=ifwego.co" rel="noreferrer">Sprinter Health</a>), gave me this advice for working more efficiently when your project gets blocked or iteration times are slow: <strong>Have a secondary workstream that you can switch to when your primary one is blocked.</strong></p><p>Getting blocked on work causes all sorts of issues for engineers: it&apos;s frustrating, it forces context switches, and it kills momentum. Personally, it&apos;s extremely frustrating and emotionally draining to have a high-pri task stuck in a way where I can&apos;t make progress. It feels like busy-waiting, a particularly expensive way a computer can wait for another task to finish by running in a loop constantly checking if it&apos;s done. Having a second workstream that you can jump to helps alleviate the problem by removing the question of &quot;<em>What should I be doing right now?</em>&quot; Instead of having to spend time and energy picking the next most important thing, you can pick it beforehand. I call this my &quot;B-track&quot;. My main workstream is my &quot;A-track&quot; and my secondary is my &quot;B-track&quot;. Instead of prioritizing everything I could be doing against everything else I could be doing, I only have to decide when the marginal utility of working on my A-track has dropped below the marginal utility of working on my B-track.</p><p>Here are some examples of when I might switch from my A-track to my B-track:</p><ul><li><strong>Waiting for code to be reviewed. </strong>This might cause a few hours to a few days of downtime depending on how complex the changes were. A lot of that time is spent waiting in asynchronous back and forth conversations about feedback on diffs, where it&apos;s more efficient to leave your changes as they are and work on something else while you wait for your reviewers to have time to take a look. As a side-effect, it keeps the pipeline primed: if your B-track work advances enough to produce another diff, you can get that next review cooking asynchronously while you follow up on your A-track diffs.</li><li><strong>Waiting for a deliverable from another teammate.</strong> This could be waiting to review something with a designer, or waiting for a decision on some feature from a PM (product manager), or some other part of the process that isn&apos;t in my control. Structurally, this is similar to the code review situation, and it has the same pipeline efficiency benefits.</li><li><strong>Waiting for code to compile or infrastructure to spin up.</strong> Whenever something is rebuilding or compiling (looking at you, mobile apps and code generators), you could switch to your B-track. This one requires faster swapping between projects. Doing this requires that you have the ability to separate out your workspaces such that you can quickly swap between different workstreams: e.g. have two different version control branches checked out in different editors, or projects in different parts of the codebase (like frontend and backend). This works better when your B-track is simple and interruptible so you can switch back to your A-track quickly. Easy refactors fit the bill, as do certain process-management/accounting tasks. Things that have a lot of state or context wouldn&apos;t work here.</li></ul><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://xkcd.com/303/?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Compiling</div><div class="kg-bookmark-description"></div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://xkcd.com/s/919f27.ico" alt="Have B-Track Work"><span class="kg-bookmark-author">xkcd</span><span class="kg-bookmark-publisher">About</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://imgs.xkcd.com/comics/compiling.png" alt="Have B-Track Work"></div></a><figcaption><p><span style="white-space: pre-wrap;">This is what you&apos;re giving up if you choose to work on your B-track instead.</span></p></figcaption></figure><p>B-track work has to be something you can easily make progress on. It has to be easy to swap back and forth to without needing a long wind-up or a costly context change. Generally, it shouldn&apos;t be anything that requires deep thought since those tasks are hard to get done in short chunks. Things that are already planned and just need execution are good candidates, as is work that is more mechanical than strategic. This kind of work can be a break that lets you relax without completely dropping out of your flow state. It keeps the momentum up and helps you feel more confident about your productivity. It can be especially helpful for more senior folks who have days where they&apos;re in and out of meetings in 30 minute chunks and don&apos;t have a long block get into a flow state. It can help you feel better about what you got done in a day because the tangible progress on the B-track work gives you something to point to while your long-term work slogs on. On the other hand, if the B-track work is too complicated and requires a huge context switch, it&apos;ll knock your A-track work out of your working memory and delay it. Pick a complexity commensurate with how often you&apos;ll be switching: if your A-track is blocked for days at a time, maybe you can afford to drop context on it in favor of your B-track when you swap. On the other hand, if you&apos;re switching for five minutes at a time, you&apos;ll need something extremely rote to do.</p><p>Good B-track work needs to be less important than your A-track work. Telling your team that a workstream is on your B-track signals that they shouldn&apos;t count on that workstream getting done by a particular time. It&apos;s explicitly there to soak up the excess resources you have while working on your A-track work, but the A-track work will always be the most important. If both things are equally important, you might still get some benefit from jumping back and forth between them when they get blocked, but you don&apos;t get the mental benefits of easy prioritization. Having your B-track be on the critical path means that it creates pressure for you instead of being a pressure release valve.  On the other hand, it&apos;s a great way to get people on board with you working on something that&apos;s less urgent but more interesting. Hearing that it&apos;s strictly secondary to your primary work means that team leaders are less worried that your &quot;side-project&quot; will take resources away from your critical work. Instead, anything you get done on your B-track project is just extra productivity that would&apos;ve otherwise been lost, and you&apos;ve framed it as a win-win.</p><p>Ideally, B-track work is something that gives you energy and can recharge you. Cameron said that he likes to save &quot;easy wins&quot; for B-track work so that when you&apos;re discouraged by your A-track work being blocked, you can pick up small wins from your B-track work. Finding B-track work that excites you but still fits the criteria above can be challenging. I think that if we can&apos;t get to &quot;exciting&quot;, we can settle for &quot;satisfying&quot;. Here&apos;s a brainstorm of things that might qualify:</p><ul><li><strong>Reviewing other people&apos;s code</strong>, especially when you&apos;re not a critical-path reviewer. Reading other people&apos;s diffs/pull requests can help you stay up to date on other projects, and providing effective feedback quickly can help newer folks ramp up faster.</li><li><strong>Fixing a small bug</strong> that bothers you, but isn&apos;t high-pri enough to be A-track.</li><li><strong>Improving the quality of the codebase</strong> by adding tests, improving alerting, refactoring things that have become unwieldy, fixing broken tests, or addressing build or runtime warnings that everyone has secretly learned to ignore. Alternatively, you could do non-coding things that improve technical quality like improving documentation or providing support to other teams that depend on your team&#x2019;s work by answering questions in a support group. </li><li><strong>Trying something out. </strong>Sometimes you just wanna poke at an idea you have to see if it&apos;ll work such as a potential refactor or a new way to use an existing capability, and making it a B-track thing relieves the pressure of having to justify the risk of experimenting in case it doesn&apos;t work out.</li><li><strong>Getting ramped up on your next project.</strong> This could be dev environment setup work, reading documentation, sending reachouts to partners you&#x2019;re gonna work with, or reading code to get familiar with the codebase.</li><li><strong>Catching up on &#x201C;chores&#x201D;.</strong> These aren&#x2019;t necessarily exciting or intrinsically satisfying, but they&#x2019;re things you need to get done that can get stressful if they&#x2019;re allowed to build up. Things like filing expense reports, writing project update posts, and triaging tasks in the team backlog could qualify here.</li></ul><p>I don&apos;t think there&apos;s anything too novel about the idea of having something to work on when your main work is blocked, but there&apos;s something simplifying about calling out what work is A-track vs B-track that works well for me. Internally, it helps me be efficient about prioritization. Instead of having to consult a backlog every time I have a free moment, I can instead just get something done. Externally, it&apos;s a much more succinct way to communicate a fairly nuanced set of expectations.</p>]]></content:encoded></item><item><title><![CDATA[Why I Needed A Docker Container That Does Nothing.]]></title><description><![CDATA[<p>If you haven&apos;t been following gaming news recently, you may have missed that the coop-base-builder-exploration-game du jour is currently Palworld, which has been aptly described as &quot;Pok&#xE9;mon with guns.&quot;</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/02/palworld-early-access-review-in-progress_3pz3.jpg" class="kg-image" alt loading="lazy" width="1280" height="720" srcset="https://ifwego.co/content/images/size/w600/2024/02/palworld-early-access-review-in-progress_3pz3.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/02/palworld-early-access-review-in-progress_3pz3.jpg 1000w, https://ifwego.co/content/images/2024/02/palworld-early-access-review-in-progress_3pz3.jpg 1280w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">As yes, everyone&apos;s favorite legally-distinct electric rodent &quot;pal&quot;.</span></figcaption></figure><p>My friends and</p>]]></description><link>https://ifwego.co/why-i-needed-a-docker-container-that-does-nothing/</link><guid isPermaLink="false">65d652b52341d50001e4350a</guid><category><![CDATA[Tech]]></category><category><![CDATA[Projects]]></category><category><![CDATA[Games]]></category><dc:creator><![CDATA[Daniel Pok]]></dc:creator><pubDate>Wed, 21 Feb 2024 21:24:37 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1573376670329-0261ea9fde97?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDIxfHxib3h8ZW58MHx8fHwxNzA4NTQ0ODIyfDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1573376670329-0261ea9fde97?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDIxfHxib3h8ZW58MHx8fHwxNzA4NTQ0ODIyfDA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Why I Needed A Docker Container That Does Nothing."><p>If you haven&apos;t been following gaming news recently, you may have missed that the coop-base-builder-exploration-game du jour is currently Palworld, which has been aptly described as &quot;Pok&#xE9;mon with guns.&quot;</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/02/palworld-early-access-review-in-progress_3pz3.jpg" class="kg-image" alt="Why I Needed A Docker Container That Does Nothing." loading="lazy" width="1280" height="720" srcset="https://ifwego.co/content/images/size/w600/2024/02/palworld-early-access-review-in-progress_3pz3.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/02/palworld-early-access-review-in-progress_3pz3.jpg 1000w, https://ifwego.co/content/images/2024/02/palworld-early-access-review-in-progress_3pz3.jpg 1280w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">As yes, everyone&apos;s favorite legally-distinct electric rodent &quot;pal&quot;.</span></figcaption></figure><p>My friends and I have been playing too, and I&apos;ve been self-hosting a server for us using Docker and Docker Compose. The Docker container image I was using is excellent (link below) and supports sending Discord webhooks whenever the server starts up or shuts down, which is surprisingly critical because the game server gets sluggish if you don&apos;t reboot it once a day or so.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/thijsvanloef/palworld-server-docker?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - thijsvanloef/palworld-server-docker: A Docker Container to easily run a Palworld dedicated server.</div><div class="kg-bookmark-description">A Docker Container to easily run a Palworld dedicated server. - thijsvanloef/palworld-server-docker</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/assets/pinned-octocat-093da3e6fa40.svg" alt="Why I Needed A Docker Container That Does Nothing."><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">thijsvanloef</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/d5f6d84d6b762e95e8c5f8f19bae602755c074da4c5676505dfdb805aefea14a/thijsvanloef/palworld-server-docker" alt="Why I Needed A Docker Container That Does Nothing."></div></a></figure><p>This is what they look like:</p><figure class="kg-card kg-image-card"><img src="https://ifwego.co/content/images/2024/02/Screenshot-2024-02-21-at-11.54.04-AM.png" class="kg-image" alt="Why I Needed A Docker Container That Does Nothing." loading="lazy" width="816" height="690" srcset="https://ifwego.co/content/images/size/w600/2024/02/Screenshot-2024-02-21-at-11.54.04-AM.png 600w, https://ifwego.co/content/images/2024/02/Screenshot-2024-02-21-at-11.54.04-AM.png 816w" sizes="(min-width: 720px) 720px"></figure><p>I wanted to expand on this to message the Discord whenever players joined or left the server, so folks could see who&apos;s online and feel tempted to hop in. I threw something together in Go that uses the linux <code>conntrack</code> module and Palworld&apos;s <code>rcon</code> server to generate join/leave events to send to Discord, and this is what they ended up looking like:</p><figure class="kg-card kg-image-card"><img src="https://ifwego.co/content/images/2024/02/Screenshot-2024-02-21-at-11.54.10-AM.png" class="kg-image" alt="Why I Needed A Docker Container That Does Nothing." loading="lazy" width="816" height="524" srcset="https://ifwego.co/content/images/size/w600/2024/02/Screenshot-2024-02-21-at-11.54.10-AM.png 600w, https://ifwego.co/content/images/2024/02/Screenshot-2024-02-21-at-11.54.10-AM.png 816w" sizes="(min-width: 720px) 720px"></figure><h2 id="what-is-conntrack">What is Conntrack?</h2><p>The <code>conntrack</code> module tracks network connections to the system and is usually used to specify firewall rules that allow packets to reach the system if they&apos;re related to a different connection that is already allowed. For example, your firewall may be configured to not allow any incoming traffic if your computer isn&apos;t hosting anything. However, when you browse the web and connect to an HTTP server, you&apos;ve gotta somehow get packets back from the server. That&apos;s where <code>conntrack</code> comes in: the outbound flow that your HTTP request creates is allowed by the firewall, then <code>conntrack</code> makes a note of that connection and records an entry saying &quot;allow incoming packets that correspond to this connection&quot;, and finally, when the HTTP server sends a response, the inbound flow will match the expected flow that <code>conntrack</code> recorded and the firewall lets it through.</p><p>That&apos;s what <code>conntrack</code> is mostly used for, but it does have one other cool trick: it can dispatch an event every time a connection changes (e.g. a new connection is created, or an existing one closes). The events look something like this:</p><pre><code class="language-log">server-manager-1  | Event[NEW](UDP) 1.2.3.4:[55533] - localhost(192.168.240.2):[8211] ([CONFIRMED, UN-REPLIED])
server-manager-1  | Event[UPDATE](UDP) 1.2.3.4:[55533] - localhost(192.168.240.2):[8211] ([REPLIED])
server-manager-1  | Event[UPDATE](UDP) 1.2.3.4:[55533] - localhost(192.168.240.2):[8211] ([ASSURED])
server-manager-1  | Event[DETROY](UDP) 1.2.3.4:[55533] - localhost(192.168.240.2):[8211] ([ASSURED][DYING])
</code></pre><p>In the example above, we can see a connection from a client (<code>1.2.3.4:55533</code>) to the server (<code>192.168.240.2:8211</code>):</p><ol><li>First we get a <code>NEW</code> event showing that we got a UDP packet in this flow which made it past the firewall (<code>CONFIRMED</code>) but the server didn&apos;t respond yet (<code>UNREPLIED</code>).</li><li>Next, we get an update event showing that the server replied (<code>REPLIED</code>).</li><li>Then, we get another update event showing that the client sent a response in reply to the server&apos;s message (<code>ASSURED</code>). This is analogous to TCP&apos;s handshake where the initiator sends a <code>SYN</code> packet, the receiver responds with a <code>SYNACK</code>, and then the initiator responds with an <code>ACK</code> and can start sending data. UDP doesn&apos;t have a handshake because it&apos;s not a stateful protocol, but <code>conntrack</code> provides these events anyway since they&apos;re broadly useful in determining when to count a connection as &quot;active&quot;. Since Palworld presumably has a connection handshake between the client and the server, the <code>ASSURED</code> state is good for us.</li><li>Finally, we get a destroy event when the client has disconnected (<code>DYING</code>). Since UDP doesn&apos;t have an explicit &quot;hangup&quot; message like TCP&apos;s <code>RST</code> packet, <code>conntrack</code> marks a UDP connection as dead if it hasn&apos;t received a packet for some amount of time (on my system, it&apos;s 180 seconds).</li></ol><h2 id="why-does-that-make-the-networking-hard">Why does that make the networking hard?</h2><p><code>conntrack</code> only tracks connections to the local system, e.g. a VM or a container. It can&apos;t track connections going to a different container. This is because linux network isolation is done with namespaces (<code>net-ns</code>), and each Docker container, by default, has its own separate network namespace. This means that if two containers both listen on the same port (say, port <code>80</code>), they can both bind to that port because they&apos;re doing it in their own namespaces. To get outside traffic to reach a container&apos;s port, it needs to be forwarded, hence why Docker supports forwarding ports from the host to a container (e.g. <code>docker run ... -p 8000:80</code>).</p><p>My server manager program in Go needed to be running in the same <code>net-ns</code> as the Palworld server, but they were running in different containers within the Docker Compose stack. I could make a new container image that ran both my server manager and the default command for the Palworld server, but that would require a more complex integration and would couple the two components together. This is roughly what the <code>docker-compose.yaml</code> looked like at this point:</p><figure class="kg-card kg-code-card"><pre><code class="language-yaml">services:
   palworld:
      image: thijsvanloef/palworld-server-docker:latest
      restart: unless-stopped
      ports:
        - 8211:8211/udp
        - 27015:27015/udp
      volumes:
         - ./data:/palworld/

   server-manager:
      image: golang:latest
      restart: unless-stopped
      depends_on:
         - palworld
      command: &quot;/src/server-manager&quot;
      cap_add:
         - NET_ADMIN
      volumes:
         - ./src:/src

</code></pre><figcaption><p><span style="white-space: pre-wrap;">docker-compose.yaml: I was too lazy to build the Go binary into its own Docker image.</span></p></figcaption></figure><p>Now, unlike in Docker Compose, when you deploy a group of containers together in Kubernetes, you can deploy them as a &quot;pod&quot;, which puts them all in the same <code>net-ns</code>. Docker Compose creates a unique <code>net-ns</code> for each container you define and creates a new virtual private network for them, so they can communicate with each other, but they each have their own network stack. If this were a Kubernetes pod, then <code>server-manager</code> would be able to monitor connections to <code>palworld:8211</code>, but not so in Docker Compose.</p><p>The I discovered this flag:  <code>--network container:&lt;container-name&gt;</code>. It&apos;s a container networking option that&apos;s a bit buried in the docs, but when you specify <code>network: container:&lt;container-name&gt;</code> in your Docker Compose service definition, Docker will put the container you&apos;re creating into the <code>net-ns</code> of the container you specified.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://docs.docker.com/network/?ref=ifwego.co#container-networks"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Networking overview</div><div class="kg-bookmark-description">Learn how networking works from the container&#x2019;s point of view</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://docs.docker.com/assets/favicons/docs@2x.ico" alt="Why I Needed A Docker Container That Does Nothing."><span class="kg-bookmark-author">Docker Documentation</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://docs.docker.com/assets/favicons/docs@2x.ico" alt="Why I Needed A Docker Container That Does Nothing."></div></a></figure><p>This kinda emulates a Kubernetes pod. Let&apos;s see the new <code>docker-compose.yaml</code> after this update:</p><figure class="kg-card kg-code-card"><pre><code class="language-yaml">services:
   palworld:
      image: thijsvanloef/palworld-server-docker:latest
      restart: unless-stopped
      ports:
        - 8211:8211/udp
        - 27015:27015/udp
      volumes:
         - ./data:/palworld/

   server-manager:
      image: golang:latest
      restart: unless-stopped
      depends_on:
         - palworld
      network: container:palworld # &lt;-- This line right here
      command: &quot;/src/server-manager&quot;
      cap_add:
         - NET_ADMIN
      volumes:
         - ./src:/src
</code></pre><figcaption><p><span style="white-space: pre-wrap;">docker-compose.yaml but with server-manager and palworld in the same net-ns.</span></p></figcaption></figure><h2 id="and-then-there-were-more-problems">And then there were more problems...</h2><p>Putting <code>server-manager</code> in <code>palworld</code>&apos;s <code>net-ns</code> worked well for a while: <code>server-manager</code> was able to use <code>conntrack</code> to monitor UDP connections to the Palworld game server port. However, after about a day, I&apos;d suddenly stop getting messages when people joined and left unless I restarted <code>server-manager</code> manually. Suspiciously, it would also fail to connect to the <code>rcon</code> server the game binary exposed on <code>127.0.0.1:25575</code> even though I could still connect to it from other containers in the same stack (that I didn&apos;t include the <code>docker-compose.yaml</code> examples above).</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/02/Screenshot-2024-02-21-at-12.33.54-PM.png" class="kg-image" alt="Why I Needed A Docker Container That Does Nothing." loading="lazy" width="1316" height="1022" srcset="https://ifwego.co/content/images/size/w600/2024/02/Screenshot-2024-02-21-at-12.33.54-PM.png 600w, https://ifwego.co/content/images/size/w1000/2024/02/Screenshot-2024-02-21-at-12.33.54-PM.png 1000w, https://ifwego.co/content/images/2024/02/Screenshot-2024-02-21-at-12.33.54-PM.png 1316w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Note that long gap between 5:35pm and midnight. People were definitely on, but alas, no pings.</span></figcaption></figure><p>Remember when I said that the server needed to be rebooted regularly? I noticed in the logs that this all started happening after it rebooted. The server status messages in the screenshot above come from the <code>palworld</code> container. When its scheduled restart time comes, it shuts down the server which causes the Docker container to exit. Because the service has <code>restart: unless-stopped</code> in its definition, Docker will then automatically recreate the container.</p><p>Turns out, when this happens, the new <code>palworld</code> container gets a new <code>net-ns</code>, but since the <code>server-manager</code> container hasn&apos;t restarted, it doesn&apos;t get connected to the new <code>palworld</code> container&apos;s <code>net-ns</code>: it&apos;s orphaned on the <code>net-ns</code> of the container that shut down. Thus, <code>conntrack</code> doesn&apos;t get any new connection events, and <code>rcon</code> can&apos;t reach the server on <code>127.0.0.1</code> (<code>localhost</code>).</p><p>If I wanted to keep the <code>palworld</code> container separate from the <code>server-manager</code> container so that they could be restarted or updated independently, I&apos;d need to find a way to keep both of them in the same <code>net-ns</code> even if one or the other restarted.</p><h2 id="much-ado-about-doing-nothing">Much ado about doing nothing.</h2><p>So my galaxy-brain plan was this:</p><ol><li>Add a third container to the <code>docker-compose.yaml</code> that would not restart.</li><li>Connect both the <code>palworld</code> container and the <code>server-manager</code> container to that container&apos;s <code>net-ns</code>.</li><li>Now, whenever <code>palworld</code> or <code>server-manager</code> restarts, they&apos;d get connected back to the same <code>net-ns</code>.</li></ol><p>I needed a container to do nothing, but in a reliable, easy-to-understand way. There are hacks for doing this using a shell, but it seemed like a pretty heavy way to do it. The best named shell-hack I found is named <code>cat</code>-abuse: you run <code>cat</code>, which will listen for input from <code>stdin</code> until it reaches an <code>EOF</code> marker, which will never happen if you don&apos;t attach to the container or otherwise feed it input, so it&apos;ll wait forever. I&apos;ve been learning Go recently since a friend was passionate about teaching me, so I figured let&apos;s give that a try.</p><p>Enter, the world&apos;s most useless Go program:</p><pre><code class="language-go">package main

import (
	&quot;log&quot;
	&quot;os&quot;
	&quot;os/signal&quot;
)

func main() {
	log.Printf(&quot;Waiting forever. Ctrl + C to interrupt...\n&quot;)

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt)

	&lt;-sigChan
	log.Printf(&quot;Got SIGINT, exiting.\n&quot;)
}
</code></pre><p>This program registers a handler for <code>SIGINT</code>, which is the signal sent to the process when you type <code>Ctrl + C</code> in a shell. It creates a Go channel that will receive a message when the program receives a <code>SIGINT</code>, then it waits until it receives that message. It blocks until it does, so unless you explicitly send a <code>SIGINT</code> to the container, it&apos;ll wait &quot;forever&quot;.</p><p>Technically, the signal handler isn&apos;t even necessary. You could block forever waiting for a message on a channel that no one will ever send to. You don&apos;t need to pass it to a handler that could conceivably send a message. I just hooked up <code>SIGINT</code> to make it more explicit that it&apos;s not supposed to stop waiting unless the program stops. You could simplify it, but I think it&apos;s less elegant.</p><figure class="kg-card kg-code-card"><pre><code class="language-go">package main

func main() {
	sigChan := make(chan struct{})
	&lt;-sigChan
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">In Go, the empty struct (struct{}) takes up no space and is used as a dummy type when you need a type that doesn&apos;t store any data.</span></p></figcaption></figure><p>Next, I added a <code>Dockerfile</code> to build the image, in as lean a way as possible:</p><figure class="kg-card kg-code-card"><pre><code class="language-Dockerfile">FROM golang:1.22.0
COPY ./src /src
WORKDIR /src
RUN go build -o /bin/wait-forever ./main.go

FROM scratch
COPY --from=0 /bin/wait-forever /bin/wait-forever
CMD [&quot;/bin/wait-forever&quot;]
</code></pre><figcaption><p><span style="white-space: pre-wrap;">Dockerfile: This is a two-stage build. First, a container with the golang build chain compiles the source code, then we copy just the binary to a new distroless container (FROM scratch). This saves space since there are no other binaries in the final container.</span></p></figcaption></figure><p>With that in place, I could update the <code>docker-compose.yaml</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-yaml">services:
   wait-forever:
      build:
         context: ./wait-forever
      ports:
         # Forwarding for Palworld server b/c this container owns the netns
         - 8211:8211/udp
         - 27015:27015/udp

   palworld:
      image: thijsvanloef/palworld-server-docker:latest
      restart: unless-stopped
      network: container:wait-forever # &lt;-- Connected to wait-forever

      # Note how the port forwarding got moved to wait-forever
      # ports:
      #   - 8211:8211/udp
      #   - 27015:27015/udp
      
      volumes:
         - ./data:/palworld/

   server-manager:
      image: golang:latest
      restart: unless-stopped
      depends_on:
         - palworld
      network: container:wait-forever # &lt;-- Connected to wait-forever
      command: &quot;/src/server-manager&quot;
      cap_add:
         - NET_ADMIN
      volumes:
         - ./src:/src
</code></pre><figcaption><p><span style="white-space: pre-wrap;">docker-compose.yaml with wait-forever, palworld, and server-manager</span></p></figcaption></figure><p>Notice how the the <code>palworld</code> container no longer contains the <code>ports:</code> map that forwards ports from the host to the container. Because it has <code>network: container:wait-forever</code>, it&apos;s not allowed to forward ports. That has to be done on the container that owns the <code>net-ns</code>, which would be <code>wait-forever</code>.</p><p>With that in place, the server has been running for about a week without issues. <code>server-manager</code> pings the Discord even if the <code>palworld</code> server restarts, and I can start and stop them separately (e.g. to update <code>server-manager</code>).</p><p>I&apos;d love to dig into how <code>server-manager</code> works, but that will probably have to wait for another time. Similarly, I&apos;d put the code up, but it&apos;s not mature enough to share (or to collaborate with other folks on), so for now, I hope this post helps anyone who needs to make Docker Compose act a bit more like Kubernetes.</p>]]></content:encoded></item><item><title><![CDATA[Notes App Dev Diary #1]]></title><description><![CDATA[<p>For the first time in months, I remembered what it was like to be obsessive about my work: always trying to get in one more feature before bed and then staying up too late. It&apos;s been nice to feel that again after a long break, but even better</p>]]></description><link>https://ifwego.co/notes-app-dev-diary-1/</link><guid isPermaLink="false">65a0f5c4b60d6200019f4915</guid><category><![CDATA[Tech]]></category><category><![CDATA[Projects]]></category><dc:creator><![CDATA[Daniel Pok]]></dc:creator><pubDate>Fri, 16 Feb 2024 18:13:21 GMT</pubDate><media:content url="https://ifwego.co/content/images/2024/01/Screenshot-2024-01-12-at-3.33.35-PM.png" medium="image"/><content:encoded><![CDATA[<img src="https://ifwego.co/content/images/2024/01/Screenshot-2024-01-12-at-3.33.35-PM.png" alt="Notes App Dev Diary #1"><p>For the first time in months, I remembered what it was like to be obsessive about my work: always trying to get in one more feature before bed and then staying up too late. It&apos;s been nice to feel that again after a long break, but even better to be able to see it with fresh eyes and think about it more clearly. I suspect that I&apos;d grown habits around the way I approached my work (both the technical and non-technical aspects of it) that might have been local maxima.</p><p>In between interview prep stuff, I&apos;ve been working on a side-project to empower players in tabletop roleplaying games like D&amp;D to take notes that make campaigns more memorable. Here&apos;s a rough pitch:</p><div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-emoji">&#x1F6D7;</div><div class="kg-callout-text">Tabletop roleplaying games like D&amp;D have exploded in popularity recently, and companies have created many digital tools aimed at game masters/dungeon masters (DMs) for worldbuilding and running the game online (virtual tabletops, aka VTTs). However, there are relatively few tools for players and DMs to take notes during a session or manage their shared understanding of the game world afterwards.<br><br>Since the game world is defined by what happens at the table, good notes should lead to more immersive worlds and memorable stories.</div></div><p>For a start, I&apos;m trying to empower three particular players since my friends and I have struggled to manage our <strong><em>one hundred forty-two page </em></strong>Google Doc where we keep the last three years of notes about our ongoing campaign. The loading time sucks. Searchability is surprisingly bad because no one spells the names of fantasy NPCs (non-player characters) correctly or consistently. Answering questions like &quot;<em>When did we last talk to this person?</em>&quot; or &quot;<em>How many days has it been since we left town?</em>&quot; can take a while to answer, which breaks immersion.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/02/page-1-of-142.png" class="kg-image" alt="Notes App Dev Diary #1" loading="lazy" width="194" height="116"><figcaption><span style="white-space: pre-wrap;">You never want to see this when opening a document.</span></figcaption></figure><p>I&apos;ve talked to these friends for years about wanting to do something about it, and this is me taking a swing at it.</p><p>I drafted this dev diary after my first week working on the notes app, and I&apos;m sharing it now just for fun &#x2013; nothing is ready for folks to play with yet. Here are my miscellaneous learnings from that week.</p><h2 id="flow-is-fun-decision-making-is-draining">Flow is fun, decision making is draining.</h2><p>The best part of the process is when you know exactly what you want and get to chip away at it piece by piece until it&apos;s done. Most coding problems get me into that flow state; even though coding requires making  a lot of small decisions, it feels like solving a puzzle. Solving simple puzzles are more fun than work. The reward of solving it is greater than the cost of the exertion, and it makes you feel smart. However, making challenging decisions is draining for me to the point where it&apos;s demotivating.</p><p>I think what differentiates the easy decisions from the hard ones is that easy decisions have a clearer &quot;right&quot; answer, provide feedback sooner, and have better understood costs for correcting them.</p><p>For example, implementing a straightforward UI component can be fun because you get quick feedback and an obvious way to iterate. It&apos;s almost like sculpting in that you can get the basic shapes in quickly, then iterate until it&apos;s right. Slap some <code>&lt;div&gt;</code>&apos;s in there, add some basic CSS, and then take a look. Looks too crowded? Add some white space. Font isn&apos;t legible? Make it bigger (or better yet: work on your <a href="https://www.nngroup.com/articles/visual-hierarchy-ux-definition/?ref=ifwego.co" rel="noreferrer">visual hierarchy</a>). Most of the time, you know what you can do to make it better, and you can do it in small chunks.</p><p>Many technical problems look this way too: Writing a parser? Make it simple then iterate on it until it&apos;s better. Need an ID service? Write one that hands out sequential numbers and wait to refactor until you need one that can shard across multiple clients. Need a client-server protocol? Bash out something naive that causes conflicting writes to clobber each other and wait until your product has <em>n &gt; 1</em> users before you pull out the CRDTs.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/02/how-about-sequential-numbers.png" class="kg-image" alt="Notes App Dev Diary #1" loading="lazy" width="2000" height="798" srcset="https://ifwego.co/content/images/size/w600/2024/02/how-about-sequential-numbers.png 600w, https://ifwego.co/content/images/size/w1000/2024/02/how-about-sequential-numbers.png 1000w, https://ifwego.co/content/images/size/w1600/2024/02/how-about-sequential-numbers.png 1600w, https://ifwego.co/content/images/2024/02/how-about-sequential-numbers.png 2282w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Technically speaking, an incrementing number is a monotonically increasing clock, and since JS is single-threaded, there&apos;s no concurrency problems *(as long as my laptop is the only client), and the throughput of a sequential ID generator is unparalleled, and the JS &quot;number&quot; type can represent integers up to 2^53-1 without loss of precision in its 52 bit mantissa, and... None of that matters. The goal was to assign IDs for a single client because I knew that including the abstraction of an ID service would be worth it, even if its implementation was naive.</span></figcaption></figure><div class="kg-card kg-toggle-card" data-kg-toggle-state="close">
            <div class="kg-toggle-heading">
                <h4 class="kg-toggle-heading-text"><span style="white-space: pre-wrap;">&#x1F43F;&#xFE0F; Aside: CRDT stands for &quot;</span><b><strong style="white-space: pre-wrap;">Conflict-free Replicated Data-Types</strong></b><span style="white-space: pre-wrap;">&quot; </span></h4>
                <button class="kg-toggle-card-icon">
                    <svg id="Regular" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                        <path class="cls-1" d="M23.25,7.311,12.53,18.03a.749.749,0,0,1-1.06,0L.75,7.311"/>
                    </svg>
                </button>
            </div>
            <div class="kg-toggle-content"><p><span style="white-space: pre-wrap;">They&apos;re a class of data structures that support simultaneous editing like you might see in Google Docs or Figma. (Though Google Docs uses an older algorithm called &quot;Operational Transform&quot;; see the first link for more details.)</span></p><p><span style="white-space: pre-wrap;">Here are some resources:</span></p><ul><li value="1"><span style="white-space: pre-wrap;">Joseph Gentle: </span><a href="https://josephg.com/blog/crdts-are-the-future/?ref=ifwego.co" rel="noreferrer"><u><span class="underline" style="white-space: pre-wrap;">CRDTs Are The Future</span></u></a></li><li value="2"><span style="white-space: pre-wrap;">Joseph Gentle: </span><a href="https://josephg.com/blog/crdts-go-brrr/?ref=ifwego.co" rel="noreferrer"><span style="white-space: pre-wrap;">CRDTs Go Brrr</span></a></li><li value="3"><span style="white-space: pre-wrap;">Martin Kleppman (author of </span><i><em class="italic" style="white-space: pre-wrap;">Designing Data Intensive Applications</em></i><span style="white-space: pre-wrap;">) gave a talk: </span><a href="https://martin.kleppmann.com/2020/07/06/crdt-hard-parts-hydra.html?ref=ifwego.co" rel="noreferrer"><span style="white-space: pre-wrap;">CRDTs the Hard Parts</span></a></li><li value="4"><span style="white-space: pre-wrap;">Two popular libraries that implement them: </span><a href="https://github.com/yjs/yjs?ref=ifwego.co" rel="noreferrer"><span style="white-space: pre-wrap;">yjs</span></a><span style="white-space: pre-wrap;"> and </span><a href="https://automerge.org/?ref=ifwego.co" rel="noreferrer"><span style="white-space: pre-wrap;">Automerge</span></a></li></ul></div>
        </div><p>In the case of something harder, the impact of the decision is less well known. During the week, I ran into this a few times when trying to decide which feature to build next. There&apos;s no one else on this project that knows the user&apos;s needs better than I do for the obvious reason of &quot;<em>I&apos;m the only person working on this project</em>&quot;, so when I run out of insight into the next thing to try, I&apos;m stuck. The first time this happened, the prototype was as hacky as it could be: The data model was basically non-existent since I just wanted to fake enough of it to get a feel for the types of journal entries I had built (which were basic text and markers, each of which was a single field that was managed locally with React state). I wanted to test adding a quote callout entry, which would support having two fields: the quote text and an optional attribution line (i.e. who said the quote). Creating that would require either improving my parser or adding some WYSIWYG authoring UI. Adding that UI would require some more refactoring to change my static text components to support editing.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/02/quote-composer.png" width="788" height="138" loading="lazy" alt="Notes App Dev Diary #1" srcset="https://ifwego.co/content/images/size/w600/2024/02/quote-composer.png 600w, https://ifwego.co/content/images/2024/02/quote-composer.png 788w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/02/quote-entry-example.png" width="880" height="362" loading="lazy" alt="Notes App Dev Diary #1" srcset="https://ifwego.co/content/images/size/w600/2024/02/quote-entry-example.png 600w, https://ifwego.co/content/images/2024/02/quote-entry-example.png 880w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Left: The syntax in the composer for creating a quote entry. Right: An example of how one is rendered in the UI</span></p></figcaption></figure><p>I ended doing a mix of both: I refactored my UI to support editing, but didn&apos;t quite build a full WYSIWYG composer interface.</p><div class="kg-card kg-toggle-card" data-kg-toggle-state="close">
            <div class="kg-toggle-heading">
                <h4 class="kg-toggle-heading-text"><span style="white-space: pre-wrap;">&#x1F43F;&#xFE0F; Aside: &quot;Composers&quot;</span></h4>
                <button class="kg-toggle-card-icon">
                    <svg id="Regular" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                        <path class="cls-1" d="M23.25,7.311,12.53,18.03a.749.749,0,0,1-1.06,0L.75,7.311"/>
                    </svg>
                </button>
            </div>
            <div class="kg-toggle-content"><p><span style="white-space: pre-wrap;">In Facebook (and I presume other products), the post authoring interface is called the &quot;composer&quot;. Even though most of the text fields in my app can also become editable text inputs, that particular one is special because it has to help the user create any kind of journal entry that the app supports.</span></p></div>
        </div><p>I&apos;m reminded of <a href="https://blog.logrocket.com/product-management/type-1-vs-type-2-decisions-overview-examples/?ref=ifwego.co" rel="noreferrer">Jeff Bezos&apos; system for categorizing decisions</a> into Type 1 (hard to reverse) vs Type 2 (easy to reverse). That framework is helpful for me since it removes the pressure of having to optimize every single decision, but even still, it&apos;s hard to know whether a decision is Type 1 or Type 2 when any decision I make requires committing hours of time to coding it up, and I might not know whether it&apos;s part of a &quot;magic moment&quot; until it&apos;s had days or weeks of testing.</p><h2 id="thinking-better-is-more-efficient-than-thinking-harder">Thinking &quot;better&quot; is more efficient than thinking &quot;harder&quot;.</h2><p>When I get really wrapped up in a task, I feel compelled to not stop. This gets annoying when it would be more efficient to call it a day and drop the problem from my brain instead of stewing on it. This gets especially unhelpful when it coincides with having to make a decision through some uncertainty. My desire to make progress on the problem every waking moment (or at least to keep the problem fresh in my mind so that I don&apos;t lose context) leads me to think about whatever I&apos;m stuck on 24/7. In the shower. While taking a walk. While pretending to pay attention to a YouTube video. Always. I remember there were times as a new grad where people would walk up to me and ask me a question and I&apos;d totally miss that they&apos;d said something because I was still stuck in my head and hadn&apos;t taken a step back to &quot;be a human again&quot;.</p><p>I remember that when I&apos;d have to make these sorts of tough decisions, my brain would fog up, the way that it does when I&apos;m shoved in front of a whiteboard coding problem after not having done one for too long. It&apos;s not that I can&apos;t solve it, but until I get over the mental block, it&apos;s slow going. I&apos;m starting to think that this is more of a learned reaction to these sorts of problems rather than an inherent part of how I think, and maybe the culprit is trying to keep the whole problem in my head instead of using other thinking aids like writing a document or drawing a diagram.</p><p>For example, I needed to figure out what feature to build next based on how much it would inform my product hypotheses. There wasn&apos;t an obvious answer, and it was clear that whatever I tried, it would take a while to validate. I found myself impulsively turning over all the possibilities in my head: maybe option A could be justified because of this, or maybe option B ought to supersede it because of some other principle. Eventually I had to tell myself to stop thinking about it because I was draining my mental &quot;juice&quot; running the same thoughts over and over again in my head like a mercury delay line: spinning and repeating in an attempt to prevent the thought from decaying, but without the focused thinking needed to distill it and make forward progress.</p><p>I brainstormed some ideas for how to spot and break these cycles:</p><ul><li><strong>Identify when I can&apos;t make any more forward progress, and put the problem down for a bit. </strong>It might be clearer in the morning, or some inspiration might strike from another place.</li><li><strong>Write things down instead of trying to keep it in my head.</strong> Don&apos;t waste valuable brain juice on keeping things remembered. Make it OK to stop thinking about the problem because you&apos;ve committed it to a place where you can easily get caught back up. (And who knows, maybe the act of writing will lead to clarity.)</li><li><strong>Pay attention to my body and do the things I know I need to do to take care of myself</strong>, even when I don&apos;t feel like doing them because I&apos;m hyperfocused on a problem. Basic things like &quot;<em>If I eat something, my brain will clear up and I&apos;ll not feel like I have to finish every single thing right now</em>.&quot;</li><li><strong>Try to unload my brain before I go to bed. </strong>I enjoy falling asleep while chewing on a problem. That&apos;s probably unhealthy, but it usually works if what I&apos;m turning over is unimportant and I can let it fade into nonsense as I fall asleep. But it really doesn&apos;t work when I&apos;m still wound up and invested in solving the problem because instead of helping me let go and fall asleep, it keeps me wound up and anxious.</li></ul><h2 id="iterative-development-means-continuous-refactoring-and-migration">Iterative development means continuous refactoring and migration.</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/02/refactor-in-progress.png" class="kg-image" alt="Notes App Dev Diary #1" loading="lazy" width="1564" height="258" srcset="https://ifwego.co/content/images/size/w600/2024/02/refactor-in-progress.png 600w, https://ifwego.co/content/images/size/w1000/2024/02/refactor-in-progress.png 1000w, https://ifwego.co/content/images/2024/02/refactor-in-progress.png 1564w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Sometimes your refactoring is truly lazy. Step 1: Comment out code. Step 2: Fix red squiggles. Step 3: ??? Step 4: Refactor done.</span></figcaption></figure><p>I never really connected the dots that iterative development necessarily implies having to refactor things constantly. I associated refactoring with bigger code bases, not greenfield solo projects, but I found myself constantly refactoring every time I implemented another set of features. In retrospect it&apos;s pretty clear why: when prototyping, you don&apos;t know what your final goal state is, so you can&apos;t make big architectural decisions that account for everything you&apos;ll need. With the exploratory prototyping I was doing, I didn&apos;t have a clearly defined product with specs or a PRD, or even a comprehensive mock in Figma. The only sane way to implement features without overthinking them was to implement them in the barest, hackiest way possible. Just trying to make something I could poke at to vibe it out and try to find where the magic moments are. So naturally, every time I needed to build a new feature, I&apos;d have to refactor out the placeholders and hacks that were there before.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/02/this-is-as-far-as-i-got-in-figma.png" class="kg-image" alt="Notes App Dev Diary #1" loading="lazy" width="1690" height="1072" srcset="https://ifwego.co/content/images/size/w600/2024/02/this-is-as-far-as-i-got-in-figma.png 600w, https://ifwego.co/content/images/size/w1000/2024/02/this-is-as-far-as-i-got-in-figma.png 1000w, https://ifwego.co/content/images/size/w1600/2024/02/this-is-as-far-as-i-got-in-figma.png 1600w, https://ifwego.co/content/images/2024/02/this-is-as-far-as-i-got-in-figma.png 1690w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">This is as far as I got in Figma before starting to code up this iteration. I had some product explorations from before with mocks that were more ambitious, but I purposely pared down the design for this prototype to try to get a usable version out sooner. I&apos;m glad that I did. It&apos;s much easier to prioritize a small set of features than a large one.</span></figcaption></figure><p>Refactoring by itself isn&apos;t an issue. It&apos;s a normal part of all software engineering work. The more challenging part is managing migrations and consistency. This, again, is something that I had only associated with big systems. So many of my and my friends&apos; projects at work over the years have been migration related. Migrations are hard, and maintaining backwards compatibility until you&apos;re ready to cut over requires a lot of planning. Because I was refactoring the data model so much as I added new journal entry types, I started running into compatibility and migration issues even though I was only three days into developing this project.</p><p>Some changes were simple to account for: I added the ability to add titles to scenes, so I added a new <code>title</code> field and made it optional. Easy. All the previous samples kept passing typechecking and rendered just fine. Then I refactored the way text with entities is parsed (i.e. turning &quot;<code>@Abby is sus</code>&quot; into &quot;<strong><u>Abby</u></strong> is sus&quot;).</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/02/text-input.png" width="906" height="138" loading="lazy" alt="Notes App Dev Diary #1" srcset="https://ifwego.co/content/images/size/w600/2024/02/text-input.png 600w, https://ifwego.co/content/images/2024/02/text-input.png 906w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/02/text-rendered.png" width="352" height="92" loading="lazy" alt="Notes App Dev Diary #1"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Text with a tagged entity on the left, and how it&apos;s rendered, on the right.</span></p></figcaption></figure><p>Originally, I parsed it when it was rendered with a couple optimizations to make it acceptably performant for the prototype: a custom tokenizer to parse it in a single pass and some memoization. But when I wanted to implement a tag search feature, I thought it would be more efficient to parse the text when it was added to the journal and store the parsed entities with each entry. This meant that all my previous example data (both real data from testing and sample data) needed to be migrated to use the new types, which was daunting because almost every field that used to be a <code>string</code> was now an object with multiple properties that required parsing to accurately spit out.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/02/abby-is-sus-json-before-1.png" width="578" height="276" loading="lazy" alt="Notes App Dev Diary #1"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/02/abby-is-sus-json-after-1.png" width="826" height="810" loading="lazy" alt="Notes App Dev Diary #1" srcset="https://ifwego.co/content/images/size/w600/2024/02/abby-is-sus-json-after-1.png 600w, https://ifwego.co/content/images/2024/02/abby-is-sus-json-after-1.png 826w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Left: The simple JSON structure before the refactor. Right: The more complex structure post-refactor.</span></p></figcaption></figure><p>On its own, this would be a trivial refactor, and all future text entries would be created in the new format without issue. However, I had tested out the app for one real session of D&amp;D with my friends and now I had a whole session&apos;s worth of notes that I had to migrate to the new format. It was frustrating to deal with maintaining this &quot;real&quot; test data while iterating, but in the end I think it&apos;s worth it. Testing is the only way to really learn what&apos;s working, so I think this is going to force me to build better tools for managing these data migrations.</p><p>The new Typescript definitions didn&apos;t match the signatures of the data created with the old format, so not only would it not render, but the app wouldn&apos;t even build because the old data failed to type check. I ended up using a mix of regex and migration functions to make it work: regex to replace the plain strings with dummy objects that contained the raw strings but not the parsed entities (thus matching the type definitions but being semantically incorrect), and then a migration function that took in those dummy objects and re-parsed every string to get the data to be consistent.</p><p>VS Code&apos;s regex find and replace (<code>&#x2318;+F</code>, then click the <code>.*</code> in the search menu) has been a useful hammer for me many times in the past. In this case, I took my JSON file and ran this regex over it:</p><figure class="kg-card kg-code-card"><pre><code>Find:    &quot;text&quot;: &quot;(.*)&quot;\n
Replace: &quot;text&quot;: {&quot;srcText&quot;:&quot;$1&quot;,&quot;segments&quot;:[],&quot;entities&quot;: []}\n</code></pre><figcaption><p><span style="white-space: pre-wrap;">This matches JSON entries with key &quot;text&quot; and a quoted value, then captures the part in quotes using (.*) and finally replaces it with a JSON object that has the property &quot;srcText&quot; set to the captured value using $1. The rest of the object has the correct types (empty arrays), but doesn&apos;t contain real data yet.</span></p></figcaption></figure><p>With this kludge in place, the <code>.json</code> file passed type checking and I could use code to continue the migration. I just needed to run the text parser on <code>text.srcText</code> and substitute in that result instead. </p><figure class="kg-card kg-image-card"><img src="https://ifwego.co/content/images/2024/02/reparse-strings.png" class="kg-image" alt="Notes App Dev Diary #1" loading="lazy" width="1096" height="232" srcset="https://ifwego.co/content/images/size/w600/2024/02/reparse-strings.png 600w, https://ifwego.co/content/images/size/w1000/2024/02/reparse-strings.png 1000w, https://ifwego.co/content/images/2024/02/reparse-strings.png 1096w" sizes="(min-width: 720px) 720px"></figure><h2 id="testing-is-the-only-way-to-know-where-you-stand">Testing is the only way to know where you stand.</h2><p>Actually committing ideas into a prototype and then testing it is how dreams die. But it&apos;s also how great products are born. You can&apos;t solve the whole problem on paper without testing it out, so the only way to really focus on your problem is to not be in love with your solution. And the way you do that is by dashing your solution against the rocks as fast as possible. This is all pretty well-accepted dogma at this point, but it still hurts to do.</p><p>I was sure that my scene-based note taking would be the foundation for better D&amp;D notes, but then I tried it out for one session and realized that when you try to take notes on a scene, half the time you&apos;re just transcribing the scene. Sure the app can give you more structure, but it&apos;s not encouraging you to take <em>efficient</em> notes, and all the energy and attention you&apos;re putting into the app is a direct tradeoff against being present at the table with your friends. Furthermore, it turns out that for the game I&apos;m currently playing in, the story doesn&apos;t tend to play out in clean &quot;scenes&quot; that all happen at one place and time so much as it does in &quot;sequences&quot;, or strings of events, attempts, and often short scenes that are all tied together by a common goal or story beat. So that&apos;s all gonna need some tweaking.</p><p>Similarly, I was confident that using tagged entities in the notes would lead to a much better way to organize knowledge: essentially turning your session notes into wiki articles about all the things in your world. I tried prototyping the &quot;wiki&quot; pages as a tag search. It turns out: when you search for the player characters, you get almost every entry in the journal because the PCs are in every scene. Not very helpful. When you search for more obscure things, it can be a bit more helpful, but it&apos;s still a lot of barely-structured text to sift through. That was a rude awakening because I was counting on this tagging and auto-organizing feature being a differentiator over a generic text editors like Google Docs or Notion. I&apos;m glad I got this learning early: it&apos;s not enough to just dig up <em>relevant</em> notes, the real value-add is separating <em>important</em> notes from unimportant ones. I&apos;ve got to start figuring out what players are looking for when they look at their notes or a wiki in the first place. Maybe it&apos;s to remember where they met a person, or to confirm if they&apos;d ever heard a certain name before. Or maybe it&apos;s to recall the description of a city they&apos;d been to before, to keep the world feeling real and show the DM they&apos;re invested. In those cases, it&apos;s important to separate out important info like &quot;<em>The city of Mithralhelm is a Dwarven city known for crafting items out of mithral.</em>&quot; from un-interesting mentions like &quot;<em>Got to gates of Mithralhelm</em>&quot;.</p><h2 id="have-a-hypothesis-for-what-delivers-value-and-focus-on-that-to-make-decisions">Have a hypothesis for what delivers value and focus on that to make decisions.</h2><p>Two of the features I thought would be slam-dunk cornerstones of the experience turned out to need lots of tweaking: grouping notes into scenes wasn&apos;t as good a match for how things play out at the table as I thought, and organizing notes by tagging entities wasn&apos;t an automatic win like I had naively hoped. That said, I feel like these learnings pushed me further towards understanding why the things that work will actually work: taking <em>more</em> notes is not better. It&apos;s far better to be efficient at taking the most useful notes instead.</p><p>I hypothesize that there are three main ways that notes can make the game better:</p><ol><li>They improve verisimilitude by recording time and place more accurately, so that the world can develop in response to what the players do.</li><li>They help players remember key bits of lore and clues that allow them to figure out important mysteries about the world or plot.</li><li>They help players remember the most dramatic, emotional, and interesting things that happened so that they can revisit or retell it later.</li></ol><p>So while I&apos;m still a bit sad that my first swing wasn&apos;t a home run, I think my learnings ultimately support my hypothesis. There are clearly better and worse ways to take notes, which means that there&apos;s a place for a product that nudges you towards better note taking. For my next iteration, I&apos;ll have to make sure I don&apos;t conflate &quot;more notes&quot; with &quot;better notes&quot;, and learn what truly effective notes look like in practice (e.g. what new kinds of journal entries to build or how to organize the types I already have).</p><h2 id="technical-ps-making-react-components-more-efficient-with-memo">Technical PS: Making React components more efficient with <code>memo(...)</code></h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/02/react-dot-memo.png" class="kg-image" alt="Notes App Dev Diary #1" loading="lazy" width="848" height="192" srcset="https://ifwego.co/content/images/size/w600/2024/02/react-dot-memo.png 600w, https://ifwego.co/content/images/2024/02/react-dot-memo.png 848w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">...honestly I can&apos;t believe I missed this for so long, though I guess most React applications don&apos;t re-render that often unless they involve text editing.</span></figcaption></figure><p>I&apos;ve worked with React for years now, but I just learned this week that <a href="https://react.dev/reference/react/memo?ref=ifwego.co#skipping-re-rendering-when-props-are-unchanged" rel="noreferrer">React will re-render all child components when its parent re-renders</a> unless you wrap that component with <code>React.memo(...)</code>. This signals to React that your component is actually a pure function like it&apos;s supposed to be, and will not re-render your component if its props don&apos;t change. I thought this was the whole point of React, to limit the amount of re-rendering done by scoping down state updates when props don&apos;t change, but I guess it&apos;s not automatic. I&apos;ve always written React components to minimize un-necessary re-rendering with thrashy state changes, or subtle reference changes (e.g. recreating arrays or objects using spreading when they haven&apos;t changed), or lists without <code>key</code>s, but I didn&apos;t realize I had to use <code>React.memo</code> too.</p><p>I found it by using the profiler. This isn&apos;t the first time I&apos;ve prototyped a text editing experience, so I knew going into this that typing needed to feel smooth for the UX to be acceptable. After looking at the profiler results on a small data set with fewer than 20 journal entries, I tried it on a larger one with hundreds of entries to see if render times for each keystroke were scaling with the number of entries. Turns out, yes. Each entry only took a fraction of a millisecond to render, but with hundreds of them, that added up to 10+ms for a state update on every keystroke. Totally unacceptable. After adding in memoization, I saw most entries not re-render on each keystroke, taking the state update time down to &lt;1ms. More importantly, it no longer scales with the number of journal entries (at least not significantly &#x2013; technically the underlying data model manipulation does require <code>O(n)</code> scans of the journal&apos;s underlying data to update. This too can be easily fixed in a future refactor.)</p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Burning Hot Or Burning Out?]]></title><description><![CDATA[<p>A few months after I started working on my first team, my product manager sat me down and asked, &quot;Are you burning hot or burning out?&quot;. It was a week or two before Thanksgiving, and more importantly for our team, Black Friday. We were juggling a lot of</p>]]></description><link>https://ifwego.co/burning-out-or-burning-hot/</link><guid isPermaLink="false">655d02d97f8c5000015f0292</guid><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Daniel Pok]]></dc:creator><pubDate>Tue, 06 Feb 2024 00:15:59 GMT</pubDate><media:content url="https://ifwego.co/content/images/2023/12/IMG_1247-2.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://ifwego.co/content/images/2023/12/IMG_1247-2.jpg" alt="Burning Hot Or Burning Out?"><p>A few months after I started working on my first team, my product manager sat me down and asked, &quot;Are you burning hot or burning out?&quot;. It was a week or two before Thanksgiving, and more importantly for our team, Black Friday. We were juggling a lot of projects at the time since our team had formed only a few months ago, and Black Friday would be our highest traffic period for months. This presented us with a unique opportunity to test a bunch of hypotheses about our product&apos;s purchase funnel and learn how to optimize each step from awareness to checkout. I was a newly minted engineer and the first engineer my manager recruited for the team, so I was an integral part of the juggling act too.</p><p>My product manager continued,</p><blockquote><strong>&quot;I recently learned that there&apos;s a fine line between burning hot and burning out. There&apos;s nothing wrong with burning hot, but you have to be really careful to know which side of that line you&apos;re on.&quot;</strong></blockquote><p>I burned hot on that team for a while, pushing out experiment after experiment for months. We had a great half. Our team grew, and my role grew with it. Suddenly, instead of just juggling features and experiments, I was juggling projects, managing interns, and advocating for new initiatives. I was pushing myself hard, to the detriment of my health, but I was learning a lot, our team was getting recognition, and I felt like I was burning hot. In retrospect, it wasn&apos;t sustainable. I was slowly sliding down the path from burning hot to burning out, but it&apos;s hard to notice it happening when you&apos;re in the thick of it.</p><p>Three years later I was still on that team, and Fall was rolling around once again. It had been a thrashy 18 months for the team, and I was feeling it acutely as it compounded with other things going on in my life. By the time summer ended, I was working on projects on my own team and another team at the same time, mentoring an intern, and ramping up new folks. All of a sudden, I couldn&apos;t deny that I was starting to burn out, so I took some time off. Less than a week after I got back, I started feeling the burnout again.</p><p>My engineering director offered to chat with me about it, and when I explained the situation to him, he said:</p><blockquote>&quot;If you take a vacation and immediately start feeling burnt out when you get back, there are probably systemic things about the work you&apos;ve taken on and the expectations you&apos;re under that are causing you to burn out. <strong>No matter how many vacations you take, you&apos;ll always keep burning out if you don&apos;t address it. </strong>I&apos;d rather you declare bankruptcy on some of the projects you&apos;re working on now and find a more sustainable workload than have you burn out trying to do too many things at once.&quot;</blockquote><p>It took me a while to accept it, but it was good advice. I dropped some projects, sequenced projects from both teams instead of trying to do both in parallel, and eventually transitioned to work on the other team full time. The switch worked out really well for me and I managed to do better work without immediately burning out again. It made me wonder why I took so long to make a switch.</p><p>In retrospect, here are some of the reasons:</p><ul><li><strong>I was working towards a promotion, and switching teams can be risky.</strong> I had been trying to get promoted on my old team for a while, and was building up to it by taking on more and bigger projects. I had a track record of mentoring interns and new engineers on my old team, and I had built the first version of many of the surfaces that the team owned, so I had a lot of institutional knowledge about what we had tried in the past. Switching teams meant potentially complicating that story or having to prove myself to new teammates. Fortunately for me, I&apos;d already been working with my new team for a while before I switched, and my old team vouched for my work.</li><li><strong>My friends were on the old team.</strong> I had friends on the new team too, and I knew when I met them that it would be a solid team, but it&apos;s hard to let go of seeing people you&apos;ve gotten really close to every day. The way the company worked, you spent a lot of time working with your team, and the kinds of teams I like to work on are especially collaborative (and chatty), so changing teams would be a big shift in my day to day routine.</li><li><strong>It felt like I was giving up on my old team and abandoning my friends who were still there.</strong> Our team was experiencing a lot of thrash then, and had been for over a year by the time I left. Thrash like that causes a lot of stress for everyone in the org, and it can be a big impediment to the growth (and career advancement) of more junior engineers like I was at the time. It felt like maybe if we could launch a few good projects, we could get the team back to a happier place. Of course, it was never my problem to solve. As a junior engineer and a junior <em>person</em> lacking both the technical and organizational experience to navigate these sorts of issues, no amount of effort on my part would have &quot;fixed&quot; it. Addressing the team&apos;s problems required skills I lacked and triggered my anxiety in a way that put me at a disadvantage to contribute to a solution. Far more senior folks were already working to right the ship, but such change is slow and not obvious to a junior person.</li><li><strong>I was nostalgic for the &quot;good old days&quot;</strong> from the team&apos;s first 18 months, when we were riding high and having a lot of fun working together. That was the carrot dangling perpetually beyond the current period of thrash. Of course, things could never go back to the way they were. The world doesn&apos;t stay still for three years, and especially not in tech. The way the company worked was different, our product&apos;s place in the market was different, and leadership&apos;s expectations of our team were different too. If I&apos;d had the maturity to try to create something good for the moment instead of pining for the past, I might&apos;ve had a clearer idea of what was possible.</li><li><strong>I was pushing myself close to (and often beyond) my limits.</strong> There were many weeks where I felt like I was barely above water, and every moment spent outside of work was just recovering enough mental and emotional energy to drag myself through the work day. That situation made it hard to really rationally reflect. I was dependent on my routine and support from my friends on the team just to stay sane, so I was having a hard time compartmentalizing work, and I was too emotionally drained and anxious to take risks.</li></ul><p>Some of these lessons I had to learn the hard way, but as I reflect on them, I&apos;m very grateful that the people I worked with chose to mentor me through burn out instead of coming down on me for it. I value the culture that the company had of trusting that they hired the right people and making it easy to try out different teams. A culture that avoids blame views burn out as a sign of systemic failure instead of personal failure, and I think that gave me the mental space I needed to recover and learn from the experience.</p>]]></content:encoded></item><item><title><![CDATA[Tips for Visiting Taipei]]></title><description><![CDATA[<p>Last year, some friends were planning to visit Taipei and asked me for advice, so I slapped together some tips on how to get around from my trip there in May 2023. This post was sitting around collecting dust for so long that it&apos;s probably out of date,</p>]]></description><link>https://ifwego.co/tips-for-visiting-taipei/</link><guid isPermaLink="false">64b36e44861729000185ac3e</guid><category><![CDATA[Travel]]></category><dc:creator><![CDATA[Daniel Pok]]></dc:creator><pubDate>Sat, 03 Feb 2024 06:34:08 GMT</pubDate><media:content url="https://ifwego.co/content/images/2023/07/IMG_9330-1.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://ifwego.co/content/images/2023/07/IMG_9330-1.jpg" alt="Tips for Visiting Taipei"><p>Last year, some friends were planning to visit Taipei and asked me for advice, so I slapped together some tips on how to get around from my trip there in May 2023. This post was sitting around collecting dust for so long that it&apos;s probably out of date, so I figured I&apos;d just post it. I&apos;m not really qualified to give advice because I&apos;m not local nor do I go there often, but I wrote down what I figured out, and now I pass it along to you.</p><h2 id="the-basics">The Basics</h2><p>You can expand each section for more details.</p><div class="kg-card kg-toggle-card" data-kg-toggle-state="close">
            <div class="kg-toggle-heading">
                <h4 class="kg-toggle-heading-text"><span style="white-space: pre-wrap;">Same power outlets as the US (110V and plug shape). &#x26A1;&#xFE0F; &#x1F50C; &#x2705;</span></h4>
                <button class="kg-toggle-card-icon">
                    <svg id="Regular" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                        <path class="cls-1" d="M23.25,7.311,12.53,18.03a.749.749,0,0,1-1.06,0L.75,7.311"/>
                    </svg>
                </button>
            </div>
            <div class="kg-toggle-content"><p><span style="white-space: pre-wrap;">You generally won&apos;t need an adapter, though you may want to check if an outlet is explicitly labeled as 220V (e.g. there was one in my hotel bathroom).</span></p></div>
        </div><div class="kg-card kg-toggle-card" data-kg-toggle-state="close">
            <div class="kg-toggle-heading">
                <h4 class="kg-toggle-heading-text"><span style="white-space: pre-wrap;">Drive and walk on the right side of the road. &#x1F698; &#x21C6; &#x1F6B6;&#x200D;&#x2640;&#xFE0F;</span></h4>
                <button class="kg-toggle-card-icon">
                    <svg id="Regular" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                        <path class="cls-1" d="M23.25,7.311,12.53,18.03a.749.749,0,0,1-1.06,0L.75,7.311"/>
                    </svg>
                </button>
            </div>
            <div class="kg-toggle-content"><ul><li value="1"><span style="white-space: pre-wrap;">Same as US, opposite of Japan/Singapore/UK.</span></li><li value="2"><span style="white-space: pre-wrap;">People don&#x2019;t tend to jaywalk (at least in the city center). YMMV. Cars and mopeds will try not to kill you, but just barely. Watch out for yourself.</span></li></ul></div>
        </div><div class="kg-card kg-toggle-card" data-kg-toggle-state="close">
            <div class="kg-toggle-heading">
                <h4 class="kg-toggle-heading-text"><span style="white-space: pre-wrap;">Most people speak Mandarin Chinese and speak varying amounts of English. &#x1F004;&#xFE0F;&#x1F4AC;</span></h4>
                <button class="kg-toggle-card-icon">
                    <svg id="Regular" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                        <path class="cls-1" d="M23.25,7.311,12.53,18.03a.749.749,0,0,1-1.06,0L.75,7.311"/>
                    </svg>
                </button>
            </div>
            <div class="kg-toggle-content"><ul><li value="1"><span style="white-space: pre-wrap;">It&apos;s probably closer to Japan than Singapore in this regard.</span></li><li value="2"><span style="white-space: pre-wrap;">Written script is traditional Chinese.</span></li><li value="3"><span style="white-space: pre-wrap;">Like many other Asian countries, signs in Chinese are sometimes written right-to-left, and other times left-to-right.</span></li></ul></div>
        </div><div class="kg-card kg-toggle-card" data-kg-toggle-state="close">
            <div class="kg-toggle-heading">
                <h4 class="kg-toggle-heading-text"><span style="white-space: pre-wrap;">The currency is the New Taiwan Dollar, abbreviated NTD. &#x1F195; &#x1F1F9;&#x1F1FC; &#x1F4B2;</span></h4>
                <button class="kg-toggle-card-icon">
                    <svg id="Regular" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                        <path class="cls-1" d="M23.25,7.311,12.53,18.03a.749.749,0,0,1-1.06,0L.75,7.311"/>
                    </svg>
                </button>
            </div>
            <div class="kg-toggle-content"><p><span style="white-space: pre-wrap;">As of February 2024, the conversion rate is roughly 31 NTD to 1 USD.</span></p><ul><li value="1"><span style="white-space: pre-wrap;">100 NTD ~= $3</span></li><li value="2"><span style="white-space: pre-wrap;">1000 NTD ~=$30</span></li><li value="3"><span style="white-space: pre-wrap;">3000 NTD ~= $100</span></li></ul><p><span style="white-space: pre-wrap;">Credit cards and tap payments are accepted in many places, but you will likely need at least a bit of cash for various things like:</span></p><ul><li value="1"><span style="white-space: pre-wrap;">Adding value to the metro card (called an EasyCard).</span></li><li value="2"><span style="white-space: pre-wrap;">Many stalls in night markets are cash only.</span></li><li value="3"><span style="white-space: pre-wrap;">Many small stores will only accept cash or QR code payments that you can&#x2019;t use as a tourist (e.g. LINE Pay).</span></li><li value="4"><span style="white-space: pre-wrap;">It&#x2019;s not always obvious whether a store will accept credit cards (unless they have a sign somewhere), and I&#x2019;ve encountered at least one place that accepted physical cards but not Apple Pay. Bring a physical card as backup, and bring some backup cash.</span></li></ul></div>
        </div><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://g.co/finance/USD-TWD?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">USD/TWD Currency Exchange Rate &amp; News - Google Finance</div><div class="kg-bookmark-description">Get the latest United States Dollar to New Taiwan dollar (USD / TWD) real-time quote, historical performance, charts, and other financial information to help you make more informed trading and investment decisions.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://ssl.gstatic.com/finance/favicon/finance_v2_180x180.png" alt="Tips for Visiting Taipei"><span class="kg-bookmark-author">Google Finance</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://ssl.gstatic.com/finance/favicon/finance_770x402.png" alt="Tips for Visiting Taipei"></div></a></figure><div class="kg-card kg-toggle-card" data-kg-toggle-state="close">
            <div class="kg-toggle-heading">
                <h4 class="kg-toggle-heading-text"><span style="white-space: pre-wrap;">Tipping is very limited in Taiwan. &#x1F4B8; &#x1F9E7;</span></h4>
                <button class="kg-toggle-card-icon">
                    <svg id="Regular" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                        <path class="cls-1" d="M23.25,7.311,12.53,18.03a.749.749,0,0,1-1.06,0L.75,7.311"/>
                    </svg>
                </button>
            </div>
            <div class="kg-toggle-content"><p><span style="white-space: pre-wrap;">This is what I got from observing and parroting things I found on the internet, so don&#x2019;t take this as gospel because I&#x2019;m definitely not a local.</span></p><ul><li value="1"><span style="white-space: pre-wrap;">Many restaurants include a 10% service charge and tipping on top of this is not expected.</span></li><li value="2"><span style="white-space: pre-wrap;">For taxis/Uber (more on this later), extra tip is not required, however it&#x2019;s common to round up if the amount is close to a nice number (e.g. if paying a taxi in cash and the bill is 88 NTD, give them 100 NTD and tell them no need to give change). Even Uber only prompts you to tip 10-30 NTD ($0.33 - $1.00).</span></li><li value="3"><span style="white-space: pre-wrap;">Tip hotel housekeeping staff by leaving money on the pillow the end of the stay. Money on the pillow is a signal that this is a tip. Unsure what an appropriate amount is, check the internet.</span></li><li value="4"><span style="white-space: pre-wrap;">Also it may be good to tip bellhops if they bring your bag to your room, but idk how or how much.</span></li></ul></div>
        </div><div class="kg-card kg-toggle-card" data-kg-toggle-state="close">
            <div class="kg-toggle-heading">
                <h4 class="kg-toggle-heading-text"><span style="white-space: pre-wrap;">Cafes and restaurants all have different procedures for paying the bill. &#x1F9FE;&#x270D;&#xFE0F; &#x1F937;&#x200D;&#x2642;&#xFE0F;</span></h4>
                <button class="kg-toggle-card-icon">
                    <svg id="Regular" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                        <path class="cls-1" d="M23.25,7.311,12.53,18.03a.749.749,0,0,1-1.06,0L.75,7.311"/>
                    </svg>
                </button>
            </div>
            <div class="kg-toggle-content"><ul><li value="1"><span style="white-space: pre-wrap;">Often, they&#x2019;ll seat you first and take your order, then you walk up to the counter to pay when you&#x2019;re done.</span></li><li value="2"><span style="white-space: pre-wrap;">At some cafes, they&#x2019;ll seat you first, then you walk over to the counter to order and pay.</span></li><li value="3"><span style="white-space: pre-wrap;">At other places (e.g. small restaurants in night markets), you may have to order and pay in the front where they make the food, then seat yourself.</span></li><li value="4"><span style="white-space: pre-wrap;">Sometimes, they&#x2019;ll come to your table when you&#x2019;re done and take payment at the table with a credit card reader (though my impression is that this is less common).</span></li><li value="5"><span style="white-space: pre-wrap;">When in doubt, just ask. It&#x2019;s not as standard as in the US.</span></li></ul></div>
        </div><h2 id="things-to-do">Things To Do</h2><p>Honestly I&apos;m not a great guide for what to do in Taipei as I usually have other obligations to attend to when I&apos;m there, so any free time I have is spent shopping and trying new food.</p><h3 id="national-palace-museum">National Palace Museum</h3><p>If there&apos;s one cultural thing I&apos;d recommend, it&apos;s seeing the National Palace Museum. It&apos;s one of the world&apos;s largest collections of Chinese historical art and artifacts. But also, it&apos;s Taiwan so their most famous items are two pieces of jade that look like food. Be warned, it&apos;s a bit far from the city center, so it&apos;ll take a while to get to and from, but it&apos;s reachable by public transit.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://maps.app.goo.gl/4rDcfN3YGwFtp6fQ6?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">National Palace Museum &#xB7; No. 221, Sec 2, Zhi Shan Rd, Shilin District, Taipei City, Taiwan 111</div><div class="kg-bookmark-description">&#x2605;&#x2605;&#x2605;&#x2605;&#x2605; &#xB7; History museum</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.google.com/images/branding/product/ico/maps15_bnuw3a_32dp.ico" alt="Tips for Visiting Taipei"><span class="kg-bookmark-author">National Palace Museum &#xB7; No. 221, Sec 2, Zhi Shan Rd, Shilin District, Taipei City, Taiwan 111</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://lh5.googleusercontent.com/p/AF1QipOFt3iY-s4FKSgjj7oV_4CdZxMtHCv70QfHT9EI=w900-h900-k-no-p" alt="Tips for Visiting Taipei"></div></a></figure><h3 id="taipei-101">Taipei 101</h3><p>The area around Taipei 101 is a shopping and office district. There are restaurants and shops around it, and it&apos;s a decent place to walk around for a bit, but I&apos;m not sure what else to do around there besides eat and shop. There&apos;s probably an observation deck you can visit too but it&apos;s not really my thing. I feel like I pop by when I visit Taipei just out of obligation, but it&apos;s not that memorable.</p><h3 id="ximen-%E8%A5%BF%E9%96%80">Ximen (&#x897F;&#x9580;)</h3><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.google.com/maps/place/Ximen/@25.0428895,121.5039556,16.66z/data=!4m6!3m5!1s0x3442a909804e4613:0x82ad6129e5d1e9b7!8m2!3d25.0421884!4d121.5082995!16s%2Fg%2F1hhky6ymd?entry=tts&amp;shorturl=1&amp;ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Ximen &#xB7; No. 32-1, Baoqing Rd, Zhongzheng District, Taipei City, Taiwan 100</div><div class="kg-bookmark-description">&#x2605;&#x2605;&#x2605;&#x2605;&#x2606; &#xB7; Subway station</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.google.com/images/branding/product/ico/maps15_bnuw3a_32dp.ico" alt="Tips for Visiting Taipei"><span class="kg-bookmark-author">Ximen &#xB7; No. 32-1, Baoqing Rd, Zhongzheng District, Taipei City, Taiwan 100</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://lh5.googleusercontent.com/p/AF1QipMjWcayZ1rLCuisJFp5D56_rc10rB0WjdnMYFEA=w900-h900-k-no-p" alt="Tips for Visiting Taipei"></div></a></figure><p>The shopping area around Ximen station is probably more touristy/hip than the bigger stores by Taipei 101. There&apos;s a lot of food, clothing, and some lifestyle stuff like stationary and arcades (?). It&apos;s a decent place to walk around.</p><h2 id="getting-around">Getting Around</h2><figure class="kg-card kg-image-card"><img src="https://ifwego.co/content/images/2023/07/IMG_7898.jpg" class="kg-image" alt="Tips for Visiting Taipei" loading="lazy" width="1502" height="2002" srcset="https://ifwego.co/content/images/size/w600/2023/07/IMG_7898.jpg 600w, https://ifwego.co/content/images/size/w1000/2023/07/IMG_7898.jpg 1000w, https://ifwego.co/content/images/2023/07/IMG_7898.jpg 1502w" sizes="(min-width: 720px) 720px"></figure><h3 id="overview">Overview</h3><ul><li>Public transit in Taipei is really good. You can get almost everywhere efficiently using the subway (called MRT) and bus system. The city also has a bike-sharing program called YouBike that&#x2019;s easy and cheap to use (once you set up the app), with good Google Maps integration.</li><li>Uber works in Taipei and integrates with the local taxi system. Getting around via taxis/Uber is cheap and efficient, and is a good way to move around the city in ways that cut across the MRT routes.</li></ul><h3 id="getting-an-easycard-the-local-metro-card">Getting an EasyCard (the local metro card)</h3><ul><li>The local metro card is called EasyCard. It&apos;s a stored-value card that holds a balance of NTD that gets deducted when you tap in and out of public transit. Like Tokyo&#x2019;s transit cards (Suica/Pasmo), you can also use them to pay at certain shops, like 7-Eleven, McDonalds, etc.</li><li>The easiest way to get an EasyCard is to buy one in an MRT station. There&#x2019;s a separate machine for getting a card vs adding value. You&#x2019;ll need cash for this, and a new card usually costs 100 NTD. This only covers the cost of the card and doesn&#x2019;t include any stored value, so you&#x2019;ll want to add some more value after that (e.g. 100-200 NTD at a time depending on how long you&#x2019;re staying).</li><li>You can also buy an EasyCard at 7-Eleven and other convenience stores. You can buy the EasyCard itself with a credit card if you buy it at a convenience store, but you&#x2019;ll still need cash to add value to the card, even in a convenience store.</li></ul><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2023/07/IMG_9081-1.jpg" width="2000" height="1910" loading="lazy" alt="Tips for Visiting Taipei" srcset="https://ifwego.co/content/images/size/w600/2023/07/IMG_9081-1.jpg 600w, https://ifwego.co/content/images/size/w1000/2023/07/IMG_9081-1.jpg 1000w, https://ifwego.co/content/images/size/w1600/2023/07/IMG_9081-1.jpg 1600w, https://ifwego.co/content/images/size/w2400/2023/07/IMG_9081-1.jpg 2400w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2023/07/IMG_9300.jpg" width="2000" height="1407" loading="lazy" alt="Tips for Visiting Taipei" srcset="https://ifwego.co/content/images/size/w600/2023/07/IMG_9300.jpg 600w, https://ifwego.co/content/images/size/w1000/2023/07/IMG_9300.jpg 1000w, https://ifwego.co/content/images/size/w1600/2023/07/IMG_9300.jpg 1600w, https://ifwego.co/content/images/size/w2400/2023/07/IMG_9300.jpg 2400w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2023/07/IMG_9064.jpg" width="2000" height="2515" loading="lazy" alt="Tips for Visiting Taipei" srcset="https://ifwego.co/content/images/size/w600/2023/07/IMG_9064.jpg 600w, https://ifwego.co/content/images/size/w1000/2023/07/IMG_9064.jpg 1000w, https://ifwego.co/content/images/size/w1600/2023/07/IMG_9064.jpg 1600w, https://ifwego.co/content/images/2023/07/IMG_9064.jpg 2064w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">These are all valid metro cards. First Image: On the bottom, a classic EasyCard. On the right, a Cinnamoroll (Sanrio) keychain that&apos;s an iPASS card type. On the top, a Gudetama (also from Sanrio) mini pancake box keychain. Second Image: Two model subway car keychains that light up and make sounds. Third Image: a Hello Kitty coffee cup keychain. It&apos;s hard to see bit in the top right corner of the cardboard backing, it shows that it&apos;s an iCash 2.0 type card.</span></p></figcaption></figure><div class="kg-card kg-toggle-card" data-kg-toggle-state="close">
            <div class="kg-toggle-heading">
                <h4 class="kg-toggle-heading-text"><span style="white-space: pre-wrap;">Types of EasyCards (You Don&apos;t Actually Need to Know This)</span></h4>
                <button class="kg-toggle-card-icon">
                    <svg id="Regular" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                        <path class="cls-1" d="M23.25,7.311,12.53,18.03a.749.749,0,0,1-1.06,0L.75,7.311"/>
                    </svg>
                </button>
            </div>
            <div class="kg-toggle-content"><p><span style="white-space: pre-wrap;">There are a few different &#x201C;types&#x201D; of EasyCards that are interchangeable, sorta like how Tokyo has Suica and Pasmo. The differences between them are negligible for a tourist, and you&#x2019;ll likely just get the normal &#x201C;EasyCard&#x201D; and not need this info at all. Regardless of what kind of card you get, it&apos;ll work on public transit and as a payment method at some convenience stores, so don&apos;t sweat it. </span></p><ul><li value="1"><b><strong style="white-space: pre-wrap;">EasyCard:</strong></b><span style="white-space: pre-wrap;"> branded with the EasyCard logo, these have the highest compatibility and work on transit, certain shops, and for YouBikes (but NOT for tourists b/c you need a local number to use an EasyCard to rent YouBikes &#x2013; see workaround below).</span></li><li value="2"><b><strong style="white-space: pre-wrap;">iPASS:</strong></b><span style="white-space: pre-wrap;"> works pretty much identically to an EasyCard</span></li><li value="3"><b><strong style="white-space: pre-wrap;">iCash 2.0:</strong></b><span style="white-space: pre-wrap;"> works on transit the same way EasyCard does, but does NOT work for YouBikes. (Again, see workaround below).</span></li></ul></div>
        </div><p>7-Eleven sells EasyCards in cooler/cuter forms than the usual credit-card form factor that the MRT station vending machines sell:</p><ul><li>There are keychains and pendants</li><li>Novelty items like miniature bottles, coffee cups, and candy boxes.</li><li>My personal favorite, a miniature version of the Taipei MRT trains that lights up and plays the door-closing chime when you scan it (using NFC power &#x2013; there&#x2019;s no battery, and you can trigger it with your phone).</li></ul><figure class="kg-card kg-video-card kg-width-regular kg-card-hascaption" data-kg-thumbnail="https://ifwego.co/content/images/2023/07/media-thumbnail-ember227.jpg" data-kg-custom-thumbnail>
            <div class="kg-video-container">
                <video src="https://ifwego.co/content/media/2023/07/IMG_9402-3.mp4" poster="https://img.spacergif.org/v1/1920x1080/0a/spacer.png" width="1920" height="1080" playsinline preload="metadata" style="background: transparent url(&apos;https://ifwego.co/content/images/2023/07/media-thumbnail-ember227.jpg&apos;) 50% 50% / cover no-repeat;"></video>
                <div class="kg-video-overlay">
                    <button class="kg-video-large-play-icon">
                        <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                            <path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/>
                        </svg>
                    </button>
                </div>
                <div class="kg-video-player-container">
                    <div class="kg-video-player">
                        <button class="kg-video-play-icon">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/>
                            </svg>
                        </button>
                        <button class="kg-video-pause-icon kg-video-hide">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"/>
                                <rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"/>
                            </svg>
                        </button>
                        <span class="kg-video-current-time">0:00</span>
                        <div class="kg-video-time">
                            /<span class="kg-video-duration">0:04</span>
                        </div>
                        <input type="range" class="kg-video-seek-slider" max="100" value="0">
                        <button class="kg-video-playback-rate">1&#xD7;</button>
                        <button class="kg-video-unmute-icon">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"/>
                            </svg>
                        </button>
                        <button class="kg-video-mute-icon kg-video-hide">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"/>
                            </svg>
                        </button>
                        <input type="range" class="kg-video-volume-slider" max="100" value="100">
                    </div>
                </div>
            </div>
            <figcaption><p><span style="white-space: pre-wrap;">This is the elevated track style train cars (sorta similar to an airport inter-terminal train), and it plays the &quot;station approaching&quot; chime.</span></p></figcaption>
        </figure><h3 id="call-a-car-ubers-and-taxis">Call A Car: Ubers and Taxis</h3><ul><li>Hailing taxis on the street is safe and convenient&#x2026; if you know how to describe where you&#x2019;re going in Chinese. Otherwise, just use Uber so that the driver gets the address without you having to explain it in Chinese. If you do try to hail a taxi, look for ones with a lit up red sign that says &#x201C;&#x7A7A;&#x8ECA;&#x201D; (empty car).<ul><li>I don&#x2019;t know how you pay for taxis these days. IIRC, they prefer cash but you can pay for most with a credit card or EasyCard.</li><li>Uber doesn&#x2019;t seem to cost much more than calling a taxi directly, so I recommend doing that.</li></ul></li><li>A 5-10 minute taxi ride can save you 30 minutes getting around on public transit if your origin or destination aren&#x2019;t close to a good MRT route. A ride of that length will cost you around 100-200 NTD (~$3-6 USD).</li><li>Often times, taxi drivers will confirm your destination (in Chinese) by checking the street and section with you. For example, you might be going to a location near Fuxing South Road, Section 2. I have no idea where my destinations are in terms of streets and sections, so I just use Uber and then nod when they ask. Dunno if that&#x2019;s the best advice, but I&apos;ve managed to get to my destination every time so far.</li></ul><h3 id="how-to-use-the-youbike-bike-sharing-system">How to Use the YouBike Bike Sharing System</h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2023/07/IMG_9301.jpg" class="kg-image" alt="Tips for Visiting Taipei" loading="lazy" width="2000" height="2041" srcset="https://ifwego.co/content/images/size/w600/2023/07/IMG_9301.jpg 600w, https://ifwego.co/content/images/size/w1000/2023/07/IMG_9301.jpg 1000w, https://ifwego.co/content/images/size/w1600/2023/07/IMG_9301.jpg 1600w, https://ifwego.co/content/images/size/w2400/2023/07/IMG_9301.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">The instructions printed inside the YouBikes. Feel free to decipher that, or just read on.</span></figcaption></figure><ul><li>There are YouBike docks stationed all over Taipei. You have to check out a bike from a dock and return it to one to complete your ride.</li><li>Google Maps knows where they are and integrates with the YouBike app to tell you how many bikes are docked at each station in real time.</li><li>Taipei has dedicated bike lanes on the sidewalks which makes it safer and more comfortable to get around by bike. It&#x2019;s a bad idea to bike on the road (unless it&#x2019;s down a small alley &#x2013; I wouldn&#x2019;t do it on major streets). The sidewalks along major streets are usually very wide, with a covered walkway extending out from the storefronts and an uncovered part on the road side. Typically, pedestrians walk in the covered part, and bikes go along the uncovered bit, but people weave all over the sidewalks, so be alert.</li><li>Locals usually register their local phone number and EasyCard so they can just tap to unlock a bike. Tourists can&#x2019;t do this without a local phone number, but they can still unlock bikes using the app.</li></ul><p>The workaround is to get the YouBike app and use an alternate setup to allow you to unlock bikes with the app for 5 days at a time (after which you&#x2019;ll have to re-register):</p><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://apps.apple.com/cn/app/youbike%E5%BE%AE%E7%AC%91%E5%96%AE%E8%BB%8A2-0-%E5%AE%98%E6%96%B9%E7%89%88/id1483423095?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">&#x200E;YouBike&#x5FAE;&#x7B11;&#x55AE;&#x8ECA;2.0 &#x5B98;&#x65B9;&#x7248;</div><div class="kg-bookmark-description">&#x200E;&#x3010; YouBike&#x5FAE;&#x7B11;&#x55AE;&#x8ECA;2.0 &#x5B98;&#x65B9;&#x7248;&#x3011; &#x958B;&#x555F;&#x4F60;&#x7684;YouBike 2.0 &#x65C5;&#x7A0B;
&#x65B9;&#x4FBF;&#x4E14;&#x5FEB;&#x901F;&#x79DF;&#x8CC3;&#x7684;24&#x5C0F;&#x6642;&#x516C;&#x5171;&#x81EA;&#x884C;&#x8ECA;&#x670D;&#x52D9;&#x7CFB;&#x7D71;&#xFF0C;
&#x7121;&#x8AD6;&#x901A;&#x52E4;&#x901A;&#x5B78;&#x3001;&#x89C0;&#x5149;&#x65C5;&#x904A;&#x3001;&#x904B;&#x52D5;&#x4F11;&#x9592;&#x96A8;&#x6642;&#x90FD;&#x80FD;&#x4EAB;&#x53D7;&#x9A0E;&#x4E58;&#x6A02;&#x8DA3;&#xFF01;
&#x4E0B;&#x8F09;YouBike 2.0 APP&#x8B93;&#x60A8;&#x5373;&#x6642;&#x638C;&#x63E1;&#x5B8C;&#x6574;&#x529F;&#x80FD;&#xFF1A;&#x5FEB;&#x901F;&#x8A3B;&#x518A;&#x6703;&#x54E1;&#x3001;&#x4EA4;&#x6613;&#x7D00;&#x9304;&#x67E5;&#x8A62;&#x3001;&#x6703;&#x54E1;&#x5361;&#x7247;&#x7BA1;&#x7406;&#x3001;&#x5373;&#x6642;&#x5834;&#x7AD9;&#x52D5;&#x614B;&#x67E5;&#x8A62;&#x3001;&#x516C;&#x5171;&#x81EA;&#x884C;&#x8ECA;&#x50B7;&#x5BB3;&#x96AA;&#x8CC7;&#x6599;&#x7DE8;&#x8F2F;&#x2026;&#x7B49;&#x7B49;&#x3002; &#x3010;&#x670D;&#x52D9;&#x5730;&#x5340;&#x3011; &#x81FA;&#x5317;&#x5E02;&#x3001;&#x65B0;&#x5317;&#x5E02;&#x3001;&#x65B0;&#x7AF9;&#x7E23;&#x3001;&#x65B0;&#x7AF9;&#x5E02;(&#x542B;&#x65B0;&#x7AF9;&#x79D1;&#x5B78;&#x5712;&#x5340;)&#x3001;&#x82D7;&#x6817;&#x7E23;&#x3001;&#x81FA;&#x4E2D;&#x5E02;&#x3001;&#x5609;&#x7FA9;&#x5E02;&#x3001;&#x53F0;&#x5357;&#x5E02;&#x3001;&#x9AD8;&#x96C4;&#x5E02;&#x3001;&#x5C4F;&#x6771;&#x7E23; &#x203B;&#x63D0;&#x9192;&#x60A8;&#x6B64;&#x70BA;YouBike 2.0 &#x5C08;&#x5C6C;APP&#xFF0C;&#x6B32;&#x67E5;&#x8A62;YouBike 1.0&#x8CC7;&#x8A0A;&#xFF0C;&#x8ACB;&#x4E0B;&#x8F09;YouBike 1.0 &#x5C08;&#x5C6C;APP&#x3002; &#x3010;&#x79DF;&#x501F;&#x6B65;&#x9A5F;&#x3011;&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://t1.gstatic.com/faviconV2?client=SOCIAL&amp;type=FAVICON&amp;fallback_opts=TYPE,SIZE,URL&amp;url=https://apps.apple.com/cn/app/youbike%E5%BE%AE%E7%AC%91%E5%96%AE%E8%BB%8A2-0-%E5%AE%98%E6%96%B9%E7%89%88/id1483423095&amp;size=128" alt="Tips for Visiting Taipei"><span class="kg-bookmark-author">App&#xA0;Store</span><span class="kg-bookmark-publisher">YouBike Co., Ltd</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://is1-ssl.mzstatic.com/image/thumb/Purple126/v4/de/d1/7f/ded17f13-4f2e-c18a-4fac-3ff0b5a7419a/AppIcon-0-0-1x_U007emarketing-0-0-0-7-0-0-sRGB-0-0-0-GLES2_U002c0-512MB-85-220-0-0.png/1200x630wa.png" alt="Tips for Visiting Taipei"></div></a><figcaption><p><span style="white-space: pre-wrap;">iOS</span></p></figcaption></figure><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://play.google.com/store/apps/details?id=tw.com.youbike.plus&amp;hl=en&amp;gl=US&amp;pli=1&amp;ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">YouBike&#x5FAE;&#x7B11;&#x55AE;&#x8ECA;2.0 &#x5B98;&#x65B9;&#x7248; - Apps on Google Play</div><div class="kg-bookmark-description">YouBike Smile Bike 2.0 Official App</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.gstatic.com/android/market_images/web/favicon_v3.ico" alt="Tips for Visiting Taipei"><span class="kg-bookmark-author">Apps on Google Play</span><span class="kg-bookmark-publisher">&#x5FAE;&#x7B11;&#x55AE;&#x8ECA;&#x80A1;&#x4EFD;&#x6709;&#x9650;&#x516C;&#x53F8;</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://play-lh.googleusercontent.com/4TNGva2Wab21rE5KjNCabyOzsUDMqR-pOydb4clw7SZ6RaDBnURJA-6ZepVpYQ2BuMY" alt="Tips for Visiting Taipei"></div></a><figcaption><p><span style="white-space: pre-wrap;">Android</span></p></figcaption></figure><ol><li>Download the app (you can pick English somewhere, I forget where).</li><li>Go to the registration page. On the bottom, there&#x2019;s a link that says &#x201C;Single Rental&#x201D;.</li><li>Confirm your email address and add a credit card. They&#x2019;ll put a ~$100 USD hold on your card as a deposit, which is refunded after the 5 days ends.</li><li>You can use the app to scan the QR code on the bike (or enter the 4 digit bike number) to unlock. On a YouBike 2.0 (the yellow ones?) there are two buttons &#x2013; a green one for unlocking with an EasyCard, and an orange one for unlocking via QR code/number. Hit the orange one and scan the QR code (or type in the 4 digit number) in the app.</li><li>Once the bike is unlocked, pull the bike back away from the dock.</li><li>Remember to re-dock the bike when you&#x2019;re done. This locks the bike and stops charging you. There&#x2019;s a metal lock block on the bike that slides into a slot in the dock.</li><li>Bike use is charged in 30 minute increments. The marginal rate goes up the longer you rent it (it&#x2019;s meant for short term rentals). The first 30 minutes costs something like $0.33 USD.</li><li>After 5 days, you may need to go through this process again.</li></ol><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2023/07/IMG_9403.PNG" width="926" height="2002" loading="lazy" alt="Tips for Visiting Taipei" srcset="https://ifwego.co/content/images/size/w600/2023/07/IMG_9403.PNG 600w, https://ifwego.co/content/images/2023/07/IMG_9403.PNG 926w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2023/07/IMG_9404.PNG" width="926" height="2002" loading="lazy" alt="Tips for Visiting Taipei" srcset="https://ifwego.co/content/images/size/w600/2023/07/IMG_9404.PNG 600w, https://ifwego.co/content/images/2023/07/IMG_9404.PNG 926w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2023/07/IMG_9405.PNG" width="926" height="2002" loading="lazy" alt="Tips for Visiting Taipei" srcset="https://ifwego.co/content/images/size/w600/2023/07/IMG_9405.PNG 600w, https://ifwego.co/content/images/2023/07/IMG_9405.PNG 926w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2023/07/IMG_9406.PNG" width="926" height="2002" loading="lazy" alt="Tips for Visiting Taipei" srcset="https://ifwego.co/content/images/size/w600/2023/07/IMG_9406.PNG 600w, https://ifwego.co/content/images/2023/07/IMG_9406.PNG 926w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2023/07/IMG_9407.PNG" width="926" height="2002" loading="lazy" alt="Tips for Visiting Taipei" srcset="https://ifwego.co/content/images/size/w600/2023/07/IMG_9407.PNG 600w, https://ifwego.co/content/images/2023/07/IMG_9407.PNG 926w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2023/07/IMG_9408.PNG" width="926" height="2002" loading="lazy" alt="Tips for Visiting Taipei" srcset="https://ifwego.co/content/images/size/w600/2023/07/IMG_9408.PNG 600w, https://ifwego.co/content/images/2023/07/IMG_9408.PNG 926w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Here are the registration steps, visually.</span></p></figcaption></figure><h1 id="quality-of-life-tips">Quality of Life Tips</h1><h3 id="dealing-with-the-heat-and-humidity">Dealing with the heat and humidity</h3><ul><li>When it&#x2019;s hot, walk slower and carry less. This sounds pretty obvious, but I tend to be a pack mule when I travel, and this doesn&apos;t work out well in the heat and humidity of Asia.</li><li>There are 7-Elevens and Family Marts everywhere, so it&#x2019;s usually easy to get a drink during the day.</li><li>Wear face sunscreen. The Biore UV sunscreen is readily available and pretty good.</li></ul><h3 id="download-some-apps-beforehand">Download some apps beforehand</h3><ul><li><strong>Google Translate </strong>(<a href="https://apps.apple.com/us/app/google-translate/id414706506?ref=ifwego.co">iOS</a>, <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.translate&amp;hl=en&amp;gl=US&amp;ref=ifwego.co">Android</a>): Download the Chinese (Traditional) offline data. The camera translation is really nice.</li><li><strong>YouBike 2.0</strong> (<a href="https://apps.apple.com/cn/app/youbike%E5%BE%AE%E7%AC%91%E5%96%AE%E8%BB%8A2-0-%E5%AE%98%E6%96%B9%E7%89%88/id1483423095?ref=ifwego.co">iOS</a>, <a href="https://play.google.com/store/apps/details?id=tw.com.youbike.plus&amp;hl=en&amp;gl=US&amp;pli=1&amp;ref=ifwego.co">Android</a>): you need this to unlock bikes, and it&#x2019;s like a 100MB download. </li><li>I recommend downloading a map of the Taipei Metro as a photo on your phone for easy no-internet access. You can download a copy <a href="https://english.metro.taipei/cp.aspx?n=1BE0AF76C79F9A38&amp;ref=ifwego.co">here</a>.</li></ul><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://english.metro.taipei/cp.aspx?n=1BE0AF76C79F9A38&amp;ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Route Map &amp; Timetables</div><div class="kg-bookmark-description"></div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www-ws.gov.taipei/001/Upload/406/sites/defaulticon/bac6920f-c281-4615-a026-012beb5ee71e.png" alt="Tips for Visiting Taipei"><span class="kg-bookmark-author">Taipei Rapid Transit Corporation &#x2500; Metro Service</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://www-ws.gov.taipei/001/Upload/FBsharelogo.jpg" alt="Tips for Visiting Taipei"></div></a></figure><h3 id="know-where-things-connect-underground-and-via-covered-walkways-above-ground">Know where things connect underground (and via covered walkways above ground).</h3><ul><li>For example, there&#x2019;s an underground mall linking the Zhongxiao Fuxing station and the Zhongxiao Dunhua station.</li></ul><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://goo.gl/maps/r75bZhiKseJbsvCM6?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">&#x6771;&#x5340;&#x5730;&#x4E0B;&#x8857; &#xB7; No. 77&#x865F;, Section 1, Da&#x2019;an Rd, Da&#x2019;an District, Taipei City, Taiwan 106</div><div class="kg-bookmark-description">&#x2605;&#x2605;&#x2605;&#x2605;&#x2606; &#xB7; Shopping mall</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.google.com/images/branding/product/ico/maps15_bnuw3a_32dp.ico" alt="Tips for Visiting Taipei"><span class="kg-bookmark-author">&#x6771;&#x5340;&#x5730;&#x4E0B;&#x8857; &#xB7; No. 77&#x865F;, Section 1, Da&apos;an Rd, Da&#x2019;an District, Taipei City, Taiwan 106</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://lh5.googleusercontent.com/p/AF1QipNBMpuDEJhsdzONQtON8NvDXkdAK2caH5qbMAzy=w900-h900-k-no-p" alt="Tips for Visiting Taipei"></div></a></figure><ul><li>There&#x2019;s a way to get from City Hall Station to Taipei 101 through indoor, underground, and elevated walkways.</li></ul><h3 id="the-non-standard-easycard-form-factors-are-really-cool">The non-standard EasyCard form factors are really cool.</h3><ul><li>Every 7-Eleven seems to have a different selection of them, so I popped into them sometimes to check for anything new or unique. They tend to be near the register. The fun ones don&#x2019;t look like cards, so it might not look the way you expect. Look for the EasyCard (or iPASS or iCash) logos on the top right of the packaging to see if something is a transit card, even if it looks like a stuffed toy.</li><li>If you get something that&#x2019;s not a credit card form factor, you can hang it on something for extra convenience. For example, if you get a keychain, you can hang it off your bag, or if you get a mini oolong tea bottle like I did, you can get a small retractable clip for it from a 7-Eleven or stationary store and clip it to your waist.</li><li>I got the mini-MRT train EasyCards in the Metro Shop inside the fare area of the Zhongxiao Fuxing MRT station. It&#x2019;s not on Google Maps as far as I know, but it&#x2019;s inside the fare area, immediately before the escalator going up to the Brown line, on the right side. It&#x2019;s immediately behind the <a href="https://goo.gl/maps/xrMPABwxs15Vcyyh7?ref=ifwego.co">Cremia</a> (a Hokkaido ice cream shop that&#x2019;s pretty decent, and is on Google Maps), but inside the fare area, so you&#x2019;ll need to go through the gate and go around.</li></ul><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2023/07/IMG_9294.jpg" width="1502" height="2002" loading="lazy" alt="Tips for Visiting Taipei" srcset="https://ifwego.co/content/images/size/w600/2023/07/IMG_9294.jpg 600w, https://ifwego.co/content/images/size/w1000/2023/07/IMG_9294.jpg 1000w, https://ifwego.co/content/images/2023/07/IMG_9294.jpg 1502w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2023/07/IMG_9295.jpg" width="1502" height="2002" loading="lazy" alt="Tips for Visiting Taipei" srcset="https://ifwego.co/content/images/size/w600/2023/07/IMG_9295.jpg 600w, https://ifwego.co/content/images/size/w1000/2023/07/IMG_9295.jpg 1000w, https://ifwego.co/content/images/2023/07/IMG_9295.jpg 1502w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Seriously, this Metro Shop is a transit nerd&apos;s dream. It&apos;s got a lot of Taipei Metro themed toys, models, accessories, etc. I got a hankerchief with the metro map on it and some gatcha balls with models of &quot;views from the Yamanote line&quot; (in Tokyo).</span></p></figcaption></figure><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://goo.gl/maps/xrMPABwxs15Vcyyh7?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">CREMIA&#x5317;&#x6D77;&#x9053;&#x51B0;&#x6DC7;&#x6DCB;&#x4E4B;&#x795E;-&#x5FE0;&#x5B5D;&#x5FA9;&#x8208;&#x5E97; &#xB7; No. 302&#x865F;, Section 3, Zhongxiao E Rd, Da&#x2019;an District, Taipei City, Taiwan 106</div><div class="kg-bookmark-description">&#x2605;&#x2605;&#x2605;&#x2605;&#x2606; &#xB7; Ice cream shop</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.google.com/images/branding/product/ico/maps15_bnuw3a_32dp.ico" alt="Tips for Visiting Taipei"><span class="kg-bookmark-author">CREMIA&#x5317;&#x6D77;&#x9053;&#x51B0;&#x6DC7;&#x6DCB;&#x4E4B;&#x795E;-&#x5FE0;&#x5B5D;&#x5FA9;&#x8208;&#x5E97; &#xB7; No. 302&#x865F;, Section 3, Zhongxiao E Rd, Da&#x2019;an District, Taipei City, Taiwan 106</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://lh5.googleusercontent.com/p/AF1QipOYFM6y2LNGiChmCJcrXY0ckNDz92hBuOx8ZHIv=w900-h900-k-no-p" alt="Tips for Visiting Taipei"></div></a><figcaption><p><span style="white-space: pre-wrap;">The &quot;secret&quot; Metro Shop is behind this Cremia. Just go into the fare area and walk slightly past the back of the Cremia and you should see it.</span></p></figcaption></figure><h3 id="bring-your-passport-with-you-when-shopping-to-get-a-tax-refund">Bring your passport with you when shopping to get a tax refund.</h3><ul><li>You can get a tax refund of about 4% on certain items you purchase that you intend to export from Taiwan.</li><li>Technically, the refund amount is higher (5%), but most department stores take a 20% processing fee, so you net out to ~4% of the purchase price refunded.</li><li>This usually only works at department stores and malls. For example, a Uniqlo or Muji in a mall will usually participate in the mall&apos;s tax refund scheme.</li><li>Most places require you to spend at least 2000 NTD (~$60) in combined receipts at that department store or mall within a single day. You can reach that amount by shopping in different shops (so individual transactions can be less than 2000 NTD), but it usually has to be on the same day.</li><li>You can only get a tax refund on items you&#x2019;re exporting (or feasibly could export). Most importantly, this means you can&#x2019;t get refunds on things like food that are consumed in Taiwan (though I think you can still get some food items refunded if you export it instead of consuming it). Things like clothes, electronics, and souvenirs are prime candidates.</li></ul><p>The process is:</p><ol><li>Bring your passport when you go shopping.</li><li>When you check out, ask if they have a tax refund. They need to prep the receipt for you to get you the refund, so let each store know when you check out. Also, they may need to split your cart into multiple transactions if some items qualify for a tax refund but others do not.</li><li>Ask where the tax refund counter is. Usually they&#x2019;ll tell you what floor and you just go find the customer service area on that floor.</li><li>Take all your receipts and the items you bought to the service counter (usually take a number, or if there&#x2019;s no line, say &#x201C;Tax Refund&#x201D; and they&#x2019;ll direct you to the correct station). Give them your passport and receipts.</li><li>They may inspect your items to make sure that you purchased what was listed and that items qualify for a tax refund.</li><li>You may get a refund in one of two forms:</li></ol><ul><li>Cash: they&#x2019;ll hand you cash on the spot</li><li>A voucher that you need to take to the tax refund counter at the airport to collect your refund when you leave.</li><li>Either way, you need to keep your documentation with you and the items that you claimed the refund on. The government does random inspections of tax refund claims at the airport to make sure people actually exported the items they claimed a tax refund for.</li></ul>]]></content:encoded></item><item><title><![CDATA[Cupping Calc: A Small Bet That Paid Off]]></title><description><![CDATA[<div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-emoji">&#x2795;</div><div class="kg-callout-text">Just looking for a link to the tool? You can find it here:<br><a href="https://polerizer.github.io/cupping/?ref=ifwego.co">https://polerizer.github.io/cupping/</a><br><br><i><em class="italic" style="white-space: pre-wrap;">It&apos;s designed for mobile, so it&apos;ll look weird on desktop.</em></i></div></div><p>I got into tech because I enjoy the feeling of being able to solve problems myself, without</p>]]></description><link>https://ifwego.co/cupping-calc/</link><guid isPermaLink="false">65bd8156b60d6200019f4b87</guid><category><![CDATA[Coffee]]></category><category><![CDATA[Projects]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Daniel Pok]]></dc:creator><pubDate>Sat, 03 Feb 2024 02:58:40 GMT</pubDate><media:content url="https://ifwego.co/content/images/2024/02/Screenshot-2024-02-02-at-5.20.49-PM.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-emoji">&#x2795;</div><div class="kg-callout-text">Just looking for a link to the tool? You can find it here:<br><a href="https://polerizer.github.io/cupping/?ref=ifwego.co">https://polerizer.github.io/cupping/</a><br><br><i><em class="italic" style="white-space: pre-wrap;">It&apos;s designed for mobile, so it&apos;ll look weird on desktop.</em></i></div></div><img src="https://ifwego.co/content/images/2024/02/Screenshot-2024-02-02-at-5.20.49-PM.png" alt="Cupping Calc: A Small Bet That Paid Off"><p>I got into tech because I enjoy the feeling of being able to solve problems myself, without needing to wait for other people to shrink-wrap and sell a solution to me. When it actually works out that way, it feels validating: I have a problem, I write a program, and <s>now I have two problems</s> I have the tool I needed to solve the problem. It&apos;s nice when it&apos;s a technical problem, but it&apos;s even better when it&apos;s something I can use &quot;in real-life&quot; &#x2013; my own small contribution to a world that feels right.</p><p>Last summer, when I was doing coffee training at Boot Coffee, I got frustrated by the process of totaling up scores when grading coffee. The process is called &quot;cupping&quot;, and I have a more detailed description of the how and why of cupping in my posts about the class. The short version is that cupping is the process of grading the quality of a coffee, and it requires adding up ten sub-scores that include qualitative assessments of the coffee across seven dimensions and technical assessments of the coffee against three sets of criteria. When cupping formally like you do for the class, you have to use the traditional paper form, which captures qualitative descriptions of the coffee in addition to the scores. However, to get certified at the end of course, you have to demonstrate that you can cup coffee accurately within time constraints. The process of adding up all ten sub-scores under pressure, while you think about and adjust them, was stressful for me. It&apos;s too easy to make an arithmetic mistake, and you really don&apos;t want to get dinged for clerical errors.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2024/02/IMG_0412.jpg" class="kg-image" alt="Cupping Calc: A Small Bet That Paid Off" loading="lazy" width="2000" height="1398" srcset="https://ifwego.co/content/images/size/w600/2024/02/IMG_0412.jpg 600w, https://ifwego.co/content/images/size/w1000/2024/02/IMG_0412.jpg 1000w, https://ifwego.co/content/images/size/w1600/2024/02/IMG_0412.jpg 1600w, https://ifwego.co/content/images/2024/02/IMG_0412.jpg 2000w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">One of the first cupping forms I filled out. It&apos;s not a great example of what a professional cupper&apos;s notes would look like, but it does reflect the iterative process of writing, erasing, and updating scores that I went through (especially as a newer cupper).</span></figcaption></figure><p>Clear goals and hard time constraints are an excellent way to get minimum viable products (MVPs). I had two weeks of courses: week one was more introductory, week two was advanced. There wasn&apos;t much room for error in the second week; I&apos;d have to have my tools and approach dialed in by the end of the first week. So by Thursday of week one, I realized that if I wanted to build something, I&apos;d have to do it that night and test it the next day.</p><p>When I got back to my hotel, I got to work. I built a prototype with Typescript, React Native, and React Native Web &#x2013; a combo that&apos;s worked well for me for prior web/native projects. For ease of distribution, I wanted to put it on the web so my classmates could try it, but for future-proofing, I used React Native in case I wanted to convert it to a native mobile app in the future.</p><p>If you want to play with it for yourself, you can do so here:</p><p><em>Note that it&apos;s designed for mobile, so it&apos;ll look weird on desktop.</em></p><div class="kg-card kg-button-card kg-align-center"><a href="https://polerizer.github.io/cupping/?ref=ifwego.co" class="kg-btn kg-btn-accent">Open Cupping Calc</a></div><h2 id="walkthrough">Walkthrough</h2><figure class="kg-card kg-video-card kg-width-regular" data-kg-thumbnail="https://ifwego.co/content/media/2024/02/Screen-Recording-2023-07-03-at-9.51.36-AM_thumb.jpg" data-kg-custom-thumbnail>
            <div class="kg-video-container">
                <video src="https://ifwego.co/content/media/2024/02/Screen-Recording-2023-07-03-at-9.51.36-AM.mp4" poster="https://img.spacergif.org/v1/902x1808/0a/spacer.png" width="902" height="1808" playsinline preload="metadata" style="background: transparent url(&apos;https://ifwego.co/content/media/2024/02/Screen-Recording-2023-07-03-at-9.51.36-AM_thumb.jpg&apos;) 50% 50% / cover no-repeat;"></video>
                <div class="kg-video-overlay">
                    <button class="kg-video-large-play-icon">
                        <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                            <path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/>
                        </svg>
                    </button>
                </div>
                <div class="kg-video-player-container">
                    <div class="kg-video-player">
                        <button class="kg-video-play-icon">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/>
                            </svg>
                        </button>
                        <button class="kg-video-pause-icon kg-video-hide">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"/>
                                <rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"/>
                            </svg>
                        </button>
                        <span class="kg-video-current-time">0:00</span>
                        <div class="kg-video-time">
                            /<span class="kg-video-duration">0:48</span>
                        </div>
                        <input type="range" class="kg-video-seek-slider" max="100" value="0">
                        <button class="kg-video-playback-rate">1&#xD7;</button>
                        <button class="kg-video-unmute-icon">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"/>
                            </svg>
                        </button>
                        <button class="kg-video-mute-icon kg-video-hide">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"/>
                            </svg>
                        </button>
                        <input type="range" class="kg-video-volume-slider" max="100" value="100">
                    </div>
                </div>
            </div>
            
        </figure><p>Here&apos;s a walkthrough:</p><ol><li>Each of the categories is listed in order.</li><li>Scores in the qualitative categories go in increments of <code>0.25</code> from <code>6.00</code> to <code>9.75</code>, so to pick a score, select a major digit (<code>6</code>, <code>7</code>, <code>8</code>, or <code>9</code>) and then select a fractional component (<code>.00</code>, <code>.25</code>, <code>.50</code>, or <code>.75</code>). As a shortcut, you can double tap the major digit to set it to <code>.00</code> (e.g. Tap <code>8</code> and then <code>8</code> again to set it to <code>8.00</code>).</li><li>Each time you finish entering in a sub-score, that section will collapse to help you focus on the ones you still need to enter, but you can tap on a section at any time to re-open it.</li><li>For the technical categories, a coffee starts with full points (10 in each) and loses 2pts for each cup in the set of five that fails to meet the criteria for that category. These categories start off collapsed by default since most high quality coffees will not have any failing cups. If you do need to mark a failing cup, tap on a category to expand it, and then tap on the &quot;&#x2705;&quot; corresponding to the cup that failed to change it to a &quot;&#x274C;&quot; (noting which cups had defects is important in cupping).</li><li>If there are defects (cups failing in any technical category are defective), tap the &quot;Defects&quot; section to expand it, select the number of cups exhibiting defects, and then select the severity of those defects (i.e. &quot;Taint (-2)&quot; or &quot;Fault (-4)&quot;).</li><li>The bottom sheet labeled &quot;Total&quot; keeps track of your score so far and includes a &quot;&#x26A0;&#xFE0F;&quot; if there are sub-scores that haven&apos;t been filled in. Tap on the bottom sheet to expand it. The expanded view includes the subtotal and defect penalty, matching the way the paper form expects you to show your work.</li><li>There&apos;s a &quot;Reset&quot; button at the bottom of the &quot;Total&quot; bottom-sheet to quickly reset the form. You can use this between samples during a cupping session instead of having to refresh the page.</li><li>If you need a refresher on this, there&apos;s a &quot;Usage Tips&quot; section right under the header that you can tap to show some help text.</li></ol><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.45.25.png" width="1179" height="2556" loading="lazy" alt="Cupping Calc: A Small Bet That Paid Off" srcset="https://ifwego.co/content/images/size/w600/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.45.25.png 600w, https://ifwego.co/content/images/size/w1000/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.45.25.png 1000w, https://ifwego.co/content/images/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.45.25.png 1179w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.53.25.png" width="1179" height="2556" loading="lazy" alt="Cupping Calc: A Small Bet That Paid Off" srcset="https://ifwego.co/content/images/size/w600/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.53.25.png 600w, https://ifwego.co/content/images/size/w1000/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.53.25.png 1000w, https://ifwego.co/content/images/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.53.25.png 1179w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.53.30.png" width="1179" height="2556" loading="lazy" alt="Cupping Calc: A Small Bet That Paid Off" srcset="https://ifwego.co/content/images/size/w600/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.53.30.png 600w, https://ifwego.co/content/images/size/w1000/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.53.30.png 1000w, https://ifwego.co/content/images/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.53.30.png 1179w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.53.33.png" width="1179" height="2556" loading="lazy" alt="Cupping Calc: A Small Bet That Paid Off" srcset="https://ifwego.co/content/images/size/w600/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.53.33.png 600w, https://ifwego.co/content/images/size/w1000/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.53.33.png 1000w, https://ifwego.co/content/images/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.53.33.png 1179w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://ifwego.co/content/images/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.53.40.png" width="1179" height="2556" loading="lazy" alt="Cupping Calc: A Small Bet That Paid Off" srcset="https://ifwego.co/content/images/size/w600/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.53.40.png 600w, https://ifwego.co/content/images/size/w1000/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.53.40.png 1000w, https://ifwego.co/content/images/2024/02/Simulator-Screenshot---iPhone-14-Pro---2023-07-03-at-09.53.40.png 1179w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Screenshots from the app.</span></p></figcaption></figure><h2 id="what-it-doesnt-do">What It Doesn&apos;t Do</h2><ul><li><strong>It doesn&apos;t store any data persistently.</strong> Once you refresh the page or hit &quot;Reset&quot;, all data is lost. It&apos;s a calculator, not a cupping data platform. There&apos;s no backend. This is intentional since I wanted to keep it as simple as possible to get the MVP done in time for me to use, and to reduce the risk of errors during the high-stakes second week of classes.</li><li><strong>It doesn&apos;t store qualitative descriptors about the coffee (e.g. flavor notes).</strong> It&apos;s just meant to replace a traditional calculator for summing up the sub-scores on the sheet. It isn&apos;t meant to replace the actual cupping form the way that some digital cupping tools do. This is because it was built for my needs  as a coffee student, where we were tested on using the paper cupping form.</li><li><strong>Teach you how to cup coffee.</strong> I put in a couple tips in the &quot;Totals&quot; bottom-sheet based on the most common feedback we got in class, but other than that, it&apos;s not meant to explain how cupping works. It assumes that you&apos;re learning from someone qualified, and are just using the app to add numbers.</li></ul><h2 id="design-iteration">Design &amp; Iteration</h2><p>While the app looks really rough, I&apos;m proud of getting it done fast enough to not only use, but iterate on and share with my classmates. Here are some of the considerations and changes I made throughout that process.</p><ul><li><strong>&quot;Brutalist&quot; design</strong>. I didn&apos;t have time to make it pretty, so I indexed on usability. The visual style I went with is simple and closely reflects the underlying components. I did it out of necessity, but I think it&apos;s in the vein of <a href="https://www.nngroup.com/articles/brutalism-antidesign/?ref=ifwego.co" rel="noreferrer">&quot;brutalist&quot; web design</a>, where a website&apos;s UI components stick closely to their raw HTML elements. I suck at picking good colors, so there are very few colors. I used brightness and size to indicate hierarchy: major digit selectors are brighter white since they&apos;re more important, decimal part selectors are a darker share to de-emphasize them, and the contrast between them provides a rhythm to the page that makes it easier to scan. The scores have a larger font size and more space around them to make them jump out. Color mostly comes from emojis, and they have very straightforward meanings: green is good (&quot;&#x2705;&quot;), red is bad (&quot;&#x274C;&quot;, Reset button), and yellow is warnings (&quot;&#x26A0;&#xFE0F;&quot;).</li><li><strong>Polishing the score selector.</strong> My thought process went like this: the most naive score selector is a textbox that limits inputs to numbers, but we know that there are only sixteen possible values so it&apos;d be better to show them visually for the user to tap instead of having to type it in (&quot;<a href="https://www.nngroup.com/articles/recognition-and-recall/?ref=ifwego.co#recognition-vs-recall" rel="noreferrer">recognition vs recall</a>&quot;). The sixteen possibilities could be laid out as a grid so it only takes one tap to pick a value, but a 4x4 grid has too many options &#x2013; giving each a suitably large tap target would make the component too big, which would make it harder to scan the page and keep context on the task. The major and minor axes of that grid (major digits vs decimal parts) are independent, so we could compromise and create a solution that takes two taps instead: one for the major digit, and one for the decimal part. It&apos;s one more tap, but since the user is already thinking of the score as &quot;seven plus zero-point-five&quot;, the second tap has minimal mental cost, and overall improves the usability over a big grid.</li><li><strong>Double-tap to select <code>.00</code>. </strong>After a day of testing the app in class, I realized that keying in round numbers like <code>8.00</code> was un-ergonomic because the <code>.00</code> option was all the way on the left edge of the screen, requiring me to move my thumb horizontally (and shifting my grip) to do so. I tried making it so that when you select a major digit, it defaults to <code>.00</code>, but this interrupted the flow in two ways: first, the section wouldn&apos;t auto-collapse if you didn&apos;t explicitly pick <code>.00</code> again since it didn&apos;t know if you were done keying in that score, and second, it meant that some scores now took one tap and others took two. When keying in ten scores, you want to make sure you didn&apos;t miss any, so the rhythm of each score taking exactly two taps provided important feedback which this change would break. I went home that night and tried something else: if you tap the major digit twice, it selects <code>.00</code> for you. This felt good in testing. It solved the ergonomic issue of having to move my finger across the screen, but preserved the rhythm of having exactly two taps per score. Usability testing FTW!</li><li><strong>Keeping things in the same order as the paper form.</strong> In my first iteration, I put the three technical categories (&quot;<em>Uniformity</em>&quot;, &quot;<em>Clean Cup</em>&quot;, and &quot;<em>Sweetness</em>&quot;) at the end of the list, after the final qualitative category, &quot;<em>Overall</em>&quot;. I put them at the end because most coffees don&apos;t have issues that require docking points on the technical categories whereas every cup must be scored in the &quot;<em>Overall</em>&quot; category. I thought it would be more streamlined to keep the less-used categories further down and collapsed by default. However, when I tested it, I found it confusing. When I keyed in the scores from my sheet, I&apos;d do it in the same order as I saw it on the form, so having the app in a different order broke my flow. The core emotional pain-point the app solves is alleviating the fear and stress of adding up my sub-scores incorrectly or too slowly, so I was very concerned about accuracy. I&apos;m likely to go box-by-box on the paper form to ensure that all those numbers match what I keyed into the app, and if they&apos;re in a different order it causes cognitive dissonance. I solved this problem by putting the technical categories in their normal spot, before the &quot;<em>Overall</em>&quot; category, but collapsing them by default. That way, a user can visually see that those categories are accounted for, but without it taking up any more space than necessary. Finally, I applied that principle to the &quot;Totals&quot; bottom-sheet by putting &quot;Subtotal&quot; above &quot;Defects&quot; to match how the &quot;Subtotal&quot; box is vertically above the &quot;Defects&quot; box on the paper form.</li><li><strong>Tips and reminders.</strong> I knew I didn&apos;t want to put too much text into the app because the physical environment of a cupping lab is already quite busy, and the app needed to be as simple and predictable as possible. I included text in a few places that I thought aligned with the goals of the app:<ul><li><strong>Small encouragements at the top of the app</strong> to remind me to break out of stress-mode, take a deep breath, and do my best. They include emojis to add a bit more playfulness to a stressful environment. The three snippets on top were quotes from our instructor, Valerian, and meant to project his earnest support while he was busy administering the test. You get a random one each time you refresh, and I leave it to you to go find them all if you&apos;d like.</li><li><strong>Reminders of common pitfalls</strong> <strong>in the &quot;Totals&quot; bottom-sheet</strong> so that folks would see them when they&apos;re done adding up their sub-scores. These are things are that you should double-check before you consider the form complete for that coffee, so it makes sense to have them there from a workflow perspective.</li><li><strong>Help text</strong> <strong>in a collapsed section under the header</strong>. I shared this app with my classmates and I wanted them to know about some of the flow-optimizing tricks I added to the app like double-tapping to key in a round number and the &quot;Reset&quot; button in the &quot;Totals&quot; bottom-sheet. </li></ul></li></ul><h2 id="how-it-was-received">How It Was Received</h2><p>By the end of the second week, half the class was using my app to total their scores.  I asked for feedback and as I recall, everyone who tried it figured out how to use it, no one found any major bugs, and several folks had feature suggestions for future iterations. It was the tool we needed, and not much more. I love the feeling of having a tool that&apos;s designed specifically for you, with your needs and quirks in mind. Even more so when it works for a community you care for, and reflects their needs and quirks too.</p><p>Some folks suggested that I expand on it to build a full-fledged cupping data platform. That would entail things like logging descriptors and flavor notes, saving data across sessions, and facilitating multiple people cupping the same coffees in a given session (e.g. aggregating their scores, etc.). I thought about it, but didn&apos;t end up pursuing it because I didn&apos;t think the market need matched what I was well-positioned to create. The main people who cup coffee are coffee roasters and coffee buyers (people who go to farms and source green coffee, not consumers buying coffee). The biggest challenge for these folks isn&apos;t adding up numbers while under time pressure. I&apos;d guess that their pain points are things like tracking which samples they&apos;re tasting given that they may have storerooms full of green coffee samples from farms, roasted samples for QC, etc. Addressing these pain points would be better handled by people with more domain knowledge of those particular roles. Sure, I could acquire that knowledge through user research and hands-on experience, but I&apos;m unsure that a better cupping interface would be a good wedge into that market anyway.</p><p>Ultimately, I feel like I spent the right amount of time on it. If I had spent a month building it, I would be bummed if it didn&apos;t turn into a full platform product, but instead I spent about half a day on it in total before calling a feature freeze to ensure it&apos;d be stable, and I feel like I got really good returns for that investment.</p><p>I don&apos;t have plans to keep working on it, but if you cup coffee and want some help doing arithmetic, feel free to use it.</p><div class="kg-card kg-button-card kg-align-center"><a href="https://polerizer.github.io/cupping/?ref=ifwego.co" class="kg-btn kg-btn-accent">Open Cupping Calc</a></div>]]></content:encoded></item><item><title><![CDATA[Coffee Day 5: This Is Supposed To Be Fun]]></title><description><![CDATA[<div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-text">&quot;<strong><em>That was supposed to be easy</em></strong>,&quot; Valerian said. &quot;<strong><em>There weren&apos;t any tricks. I don&apos;t know why you guys are overthinking it</em></strong>. <strong><em>This is supposed to be fun.</em></strong>&quot;<br><br>Why indeed.<br><br>First, some context: we were doing some comparative exercises, answering questions like &quot;</div></div>]]></description><link>https://ifwego.co/coffee-day-5-smell-with-your-feelings/</link><guid isPermaLink="false">64a27412e1d07600012e42d7</guid><category><![CDATA[Coffee]]></category><category><![CDATA[Coffee June]]></category><dc:creator><![CDATA[Daniel Pok]]></dc:creator><pubDate>Fri, 21 Jul 2023 23:28:51 GMT</pubDate><media:content url="https://ifwego.co/content/images/2023/07/IMG_0695-crop1.jpg" medium="image"/><content:encoded><![CDATA[<div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-text">&quot;<strong><em>That was supposed to be easy</em></strong>,&quot; Valerian said. &quot;<strong><em>There weren&apos;t any tricks. I don&apos;t know why you guys are overthinking it</em></strong>. <strong><em>This is supposed to be fun.</em></strong>&quot;<br><br>Why indeed.<br><br>First, some context: we were doing some comparative exercises, answering questions like &quot;Which of these coffees is sweeter?&quot; and &quot;Which has a more favorable acidity?&quot;. And we were overthinking it. We had learned that the intensity of acidity and the quality of acidity are not the same. More intensity often leads to more quality if it&apos;s a good kind of acidity, but if it&apos;s a bad kind of acidity, then more intensity just makes it more bad. We waffled between the two cups, one that clearly had more acidity, the other less, because we weren&apos;t sure if we were being tested on whether this was a &quot;good&quot; or &quot;bad&quot; kind of acidity.<br><br>Why did we suspect a trick?<br><br>Fade to day one of sensory: Valerian has a slide showing the senses. As you may recall, the senses involved in evaluating coffee are: taste, smell, and touch. There&apos;s an eye labeled &quot;Vision&quot;, an ear labeled &quot;Hearing&quot;, a tongue labeled &quot;Taste&quot;, a nose labeled &quot;Smell&quot;, and a hand labeled &quot;Touch&quot;.<br><br>&quot;What do we use to smell?&quot; Valerian asks. Everyone points to their noses.<br><br>&quot;What do we use to taste?&quot; Everyone points to their tongues.<br><br>&quot;What do we use to touch?&quot; The class takes a beat. When he did this to us in SCA Intro to Coffee, we all raised our hands. &quot;Wrong!&quot; Valerian declares with a chuckle. &quot;We touch coffee with our tongues!&quot;. <br><br>Smash cut to earlier in the day: Valerian was telling us about how he helped a cupper build confidence in her sensory abilities by asking her to find the odd coffee out in a set of three (a triangulation), but secretly all three coffees were the same. She eventually insisted that there was no difference, and she was right.<br><br>And then, a little later: as we were cupping a round of coffees, we encountered our first cup with a defect. In one of the twenty cups on the table, there was a phenolic flavor, which made it taste chemically, like pool water. Even though we had weighed and ground all the samples for the cupping ourselves, Valerian had somehow snuck in a bean spiked with phenol that produced the faulty cup. In the bustle of tasting through all five cups of each of the four samples, I had tasted the cup but missed the phenol. I was paranoid after that.<br><br>So we were wary of tricks. We knew Valerian would keep us on our toes, and we had to stay vigilant. Not overthinking it is hard. When you suddenly learn a lot of stuff, you worry that you&apos;re under-thinking it. It takes practice and experience to hone in on what matters and what doesn&apos;t, to calibrate yourself to think efficiently so that you don&apos;t wear yourself out second guessing yourself.</div></div><h2 id="it-was-friday-the-last-day-of-sca-sensory-intermediate">It was Friday, the last day of SCA Sensory Intermediate.</h2><img src="https://ifwego.co/content/images/2023/07/IMG_0695-crop1.jpg" alt="Coffee Day 5: This Is Supposed To Be Fun"><p>Ok, no more essays. Today, let&apos;s talk about some of the exercises we did to develop our sensory acuity. Most of these exercises are easiest to do at a coffee lab that has plenty of supplies and different coffees available to play with, but I added some notes about how you might be able to do some of these at home (ideally with a few friends to help set up and clean up). </p><h3 id="smell-with-your-feelings">Smell With Your Feelings</h3><p>I said before that if my brain isn&apos;t working, my nose isn&apos;t working. There&apos;s a big mental component to our ability to smell. I think that&apos;s good news: training your brain is easier than changing your cells. Through practice and intentional exposure to different smells, you can increase the range and depth of smells you can perceive.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2023/07/IMG_0687.jpg" class="kg-image" alt="Coffee Day 5: This Is Supposed To Be Fun" loading="lazy" width="2000" height="1500" srcset="https://ifwego.co/content/images/size/w600/2023/07/IMG_0687.jpg 600w, https://ifwego.co/content/images/size/w1000/2023/07/IMG_0687.jpg 1000w, https://ifwego.co/content/images/size/w1600/2023/07/IMG_0687.jpg 1600w, https://ifwego.co/content/images/size/w2400/2023/07/IMG_0687.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>The Le Nez du Caf&#xE9; kit comes in a wooden box with a really fiddly insert. It definitely looks like you spent $300 on it, which is good because that&apos;s how much it costs.</figcaption></figure><p>We used a tool called <a href="https://www.lenez.com/en/kits/coffee/revelation?ref=ifwego.co">Le Nez du Caf&#xE9;</a> to practice remembering smells. This kit contains 36 labeled scent vials, each representing one of the aromas found in coffee. Each vial has a number on it, and there&apos;s a booklet telling you the names of the aromas that correspond to each of the numbers. Just a quick semantic note: I&apos;m using the term &quot;aroma&quot; here to mean &quot;smell&quot; generically since that&apos;s the term Le Nez uses. Some of these smells are found in the dry coffee grounds (&quot;fragrance&quot;) and some in the wet coffee (&quot;aroma&quot;), and some are defects that you wouldn&apos;t want to find in any coffee. It&apos;s meant to be a training and calibration tool: having a lot of different aromas to practice with trains you to distinguish them, and having the smells in a distilled form instead of a natural one reduces variance so that we can use it as a point of reference.</p><p>That doesn&apos;t mean that the vials in the Le Nez kit evoke the most specific and accurate versions of a given aroma. Some aromas smelled more realistic, and others more &quot;fake&quot;. Aromas in real life are blends of many complex molecules, some of which don&apos;t last long, and particular mixtures of these molecules allow us to differentiate many variations of a given aroma. For example, the flavor of chocolate is common and recognizable, but we&apos;ve all encountered many different kinds of chocolates: even without going into the flavors of different cacao, Hershey&apos;s Kisses taste very different from a dark chocolate bar. We could identify them both as chocolately, but they have a very different quality both in flavor and aroma. The Le Nez kit includes a &quot;dark chocolate&quot; (#26), but it&apos;s not the best smelling chocolate you&apos;ve ever had. It&apos;s kinda generic. Ditto with the apple (#17).</p><p>Some of the aromas don&apos;t smell quite right at first, like lemon (#15). I thought that one would be straightforward: it seems like everyone in California grows lemons in their backyards, but Californians tend to grow Meyer lemons, which have a slightly different fragrance than, say, an Italian lemon variety. I&apos;m unsure what variety the Le Nez lemon (#15) is meant to resemble, but it took me a couple tries to recognize the lemon in the scent.</p><div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-text">&quot;Come on, Daniel. You should know this one. You had such a strong reaction to this smell earlier.&quot;<br><br>Valerian&apos;s staring at me across the table as I desperately try to identify the scent in the little glass bottle in front of me.<br><br>&quot;Either you were lying to me then or you&apos;re lying now. You said you had memories associated with this scent when you smelled it at lunch.&quot;<br><br>I didn&apos;t smell much. It faintly reminded me of four different options: Earth (#1), Cedar (#6), Smoke (#32), and Pipe Tobacco (#33). Every time I smelled it, it got fainter.<br><br>I was hoping one of the distinguishing aspects of the aroma would pop out. Was it a bit woodier like Cedar (#6)? Was it a bit sweet like Pipe Tobacco (#33)? A mix of sweet spice and ash like Smoke (#32)? Smelling it in isolation as I was doing, nothing stood out. After a couple weeks of sniffing bottles, I knew when a scent just wasn&apos;t gonna materialize for me, so I wrote down my best guess, Cedar (#6), and called it.<br><br>Wrong. It&apos;s Earth (#1). But even though I had memories of the smell of moist dirt from planting my garden, the Le Nez Earth (#1) aroma sometimes subconsciously reminded me of walking through the foggy redwood forests of the Santa Cruz mountains. I&apos;d have to drill those aromas again when I got back to my hotel room to reinforce my memory and lock in the differences.</div></div><p>That&apos;s a lot of setup to say that the way you practice with the Le Nez kit is to sniff each of the bottles one at a time to learn their scent and associate them with their labels. Once you&apos;ve done that a few times, you can blindly open a bottle, sniff it, and try to identify which one it is. It trains your ability to recall aromas and notice the difference between similar ones. It&apos;s much more useful for developing the general skill of remembering and analyzing smells than making sure you know exactly what a hazelnut (#29) smells like vs a walnut (#30) or a peanut (#28).</p><p>The main method that we learned for memorizing the smells was to sniff a bottle and write down what impressions and memories it evoked, even (especially) if the impression was different from what the label said. For example, almond (#27) smells Christmas-y to me. It reminds me of what I imagine an almond tart from The Great British Bake-off to smell like. It smells sweet. As another example, straw (#5) reminds me strongly of grass jelly. It reminded one of my classmates of a hay bale. I grew up drinking grass jelly. He had been on a lot of farms with hay bales. Different routes to the same place, and all individual and valid.</p><p>I&apos;d recommend trying this method out if you can get your hands on some scents. It doesn&apos;t have to be a Le Nez set (unless you&apos;re prepping for the Q). An essential oils set could work, as could a handful of jelly beans. The key is to think about and write down how that smell (or flavor, if you&apos;re doing jelly beans) presents itself to you. Don&apos;t look at what the label says. Get in touch with your own senses and familiarize yourself with how it feels when you smell something. Identifying something from scratch is very different than matching it to a label. Pay attention to where and how you sense it: Is the smell strong or faint? Does you feel it in the front or back of your nose? Does it feel sweet or salty (or bitter or sour or umami)? Does a metaphor spring to mind, like &quot;high notes&quot; vs &quot;low notes&quot;? Does it have a color?</p><p>Once you&apos;ve written down your impressions, check the label and see what you think. Smell it again and see if you can interpret it the same way as the label. Maybe you can form the same aroma object as the label with some prompting. Maybe you see where they&apos;re coming from, but there are elements of the smell that remind you more strongly of something else instead. Maybe you completely disagree with the perfumers and flavorists. That&apos;s fine too. You want to get into the practice of bringing all your aroma sensing tools to bear: your memories of other aromas, your subconscious feelings and associations, and your ability to describe the sensations happening in your nose and mouth.</p><p>Once you start doing this with some friends, things get even more interesting. We noticed that how a smell was perceived varied between people, between kits, and between days. For example, I perceive the Le Nez almond (#27) and walnut (#30) aromas as sweet and the peanut aroma (#28) as salty. One of my friends felt the opposite: he perceived peanut as sweet and almond as salty. Of course, you can&apos;t smell sweetness or saltiness. This bit about smelling taste modalities is literally all in our heads. My association with almonds was marzipan and baked goods, so I thought it was sweet. The walnut scent reminded me of my grandma&apos;s favorite ice cream, butter pecan. My association with peanuts was a snack can of salted peanuts, so I perceived it as salty. My friend might have been picturing salted almonds and peanut brittle, which would make for salty almonds and sweet peanuts. There&apos;s nothing right or wrong about it. It can be hard to differentiate certain clusters of aromas &#xA0;in the Le Nez kit (like the nuts, #27-30), so you use whatever trick works for you. If you remember one nut as sweet and one as salty, it doesn&apos;t matter how other people perceive it as long as you perceive them that way consistently.</p><p>Another weird thing: some aromas took a few days to appear. Valerian mentioned in class that sometimes we can be &quot;asleep&quot; to some aromas that need some time to &quot;wake up&quot;. The <em><a href="https://www.google.com/books/edition/Flavor_The_Science_of_Our_Most_Neglected/YUp8DAAAQBAJ?hl=en&amp;gbpv=1&amp;printsec=frontcover&amp;ref=ifwego.co">Flavor</a></em> book also talked about this: if you encounter a situation where your brain needs to detect or differentiate smells that it didn&apos;t have to before, you&apos;ll sometimes develop the ability to do so after repeated exposure. When I first smelled the kit, some aromas didn&apos;t smell like anything to me. I could faintly detect something, but it came and went and was always so faint that I couldn&apos;t really pin it. When I first opened the kit, earth (#1), tea rose (#11), blackcurrant (#14), and butter (#18) all smelled like nothing. For a while I was trying to figure out whether my kit was a dud or if my nose was. I reviewed them a few times, taking them out once every day or two to smell through each one. After a couple tries, I could smell tea rose (#11) easily, earth (#1) most of the time, and even butter (#18) very faintly, but blackcurrant (#14) eluded me for over a week. After a while, I could smell something in the blackcurrant (#14), but I couldn&apos;t place the smell or associate any impressions or memories with it. We discussed it in class for a while (a lot of folks struggled with this one). Most memorably, someone said it smelled like dried beer. After looking for each of the things people mentioned in it, I managed to identify it most of the time. It even started to take on the scent of the leaves of a berry bush. Moral of the story is that if you don&apos;t smell something the first time, don&apos;t panic. Sometimes your brain will adapt to identify it after repeated exposure.</p><p>But sometimes, it&apos;s spotty. Some days, bottles that I could smell the day before would suddenly go dark. Sometimes, I&apos;d spend an hour drilling on similar aromas and identify differentiating features about each only for things to jumble up again the next day. It got even more uneven between kits. Between our classmates, we had several different kits made at different times. Different production runs can create slightly different scents for the same aroma name, and they degrade faster or slower over time depending on how they&apos;re used and stored. Since the Q includes tests where you have to match bottles between two sets, our instructors had to get two kits from the same batch to ensure that they&apos;d smell the same. Sometimes an aroma would smell one way on one kit and then be totally different on another. Other times, an aroma would be perceptible in one kit but be too faint in the other or start smelling like a different aroma. The general takeaway is that even with practice, smell can be finicky. If you&apos;re not taking a test on it, then no worries, just know that you don&apos;t need to panic if something smells one way one day and then a bit different the next. If you are taking the Q, I&apos;d recommend calibrating on the test kits if you&apos;re able to. Even if you know your own kit by heart, the test kits may be different from yours (or even from each other, despite the same-batch protocol).</p><p>I feel like the Le Nez kit is a better training tool than an evaluation one. Practicing with it was eye-opening, but being able to recite the names, numbers, and categories of each aroma in the kit and identifying them by smell is just a cool party trick. It&apos;s really useful for exercising your sense of smell, but I wouldn&apos;t over-index on it unless you&apos;re taking the Q, in which case practicing with the kit at home is one of the easier things you can work on prior to taking the class.</p><h3 id="hello-coffee-nice-to-meet-you">Hello Coffee, Nice to Meet You</h3><p>We cupped more coffee, of course. Going through the ritual of the full SCA cupping procedure over and over again made it easier. As the motions of it got less distracting, I had more time and mental energy to focus on the flavors of the coffee instead. As with the Le Nez kit, I found that the more samples you try, the more things jump out at you. Getting to try a bunch of different coffees is important for learning how to differentiate coffees, and it&apos;s one of the benefits of taking a training at a proper coffee lab.</p><p>In this cupping, I was super thrown because there was a spiked sample (phenol) but I didn&apos;t spot it even though I tasted every cup. This was the first time I&apos;d encountered a spiked sample. I hadn&apos;t calibrated on defects (e.g. tasted them) before; I&apos;d only heard their flavors described (&quot;kinda like pool water or drinking from a garden hose&quot;). Afterwards, I was so paranoid that for days I wrote &quot;phenol&quot; every time I tasted something weird, even when it was mold, some other defect, or not a defect at all.</p><p>We also calibrated on taste modalities by identifying sweet, sour, and salty solutions at varying intensities. It sounds like it should be easy to tell them apart (and at high intensities they are), but it can be tricky to tell if something is very slightly sweet or very slightly sour, for example.</p><h3 id="welcome-to-the-red-room">Welcome to the Red Room</h3><p>The next set of exercises involved comparing unidentified samples of coffee side-by-side. Because the goal was to differentiate the coffees based on their fragrance and flavor, we did these in a room that was illuminated by a dim red light to make it harder to tell the coffees apart by color. This adds its own twist to things by forcing everyone to be in the same small room together, juggling our various accouterment. By now we each had a spittoon, a cupping spoon, a small cup (for COVID protocol), a bottle of water, a pencil, and a clipboard. Every stool in the lab was recruited to create temporary work stations in the red room since none of those items were allowed on the tables with the coffee.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2023/07/IMG_0214.jpg" class="kg-image" alt="Coffee Day 5: This Is Supposed To Be Fun" loading="lazy" width="1493" height="1796" srcset="https://ifwego.co/content/images/size/w600/2023/07/IMG_0214.jpg 600w, https://ifwego.co/content/images/size/w1000/2023/07/IMG_0214.jpg 1000w, https://ifwego.co/content/images/2023/07/IMG_0214.jpg 1493w" sizes="(min-width: 720px) 720px"><figcaption>Part of my kit for Week 1: a spittoon, clipboard, and water bottle. The clear bottle in the back contained one of the samples in the taste modalities training: an unknown quantity of sweetness, saltiness, or acidity in differing intensities.</figcaption></figure><p>Let&apos;s start with the simpler exercise: targeted comparisons. In this exercise, we had trays with two coffees each. For each pair, there was a different question: &quot;Which of these is sweeter?&quot;, &quot;Which of these has a more pleasing acidity?&quot;, &quot;Which of these tastes cleaner?&quot;. It&apos;s similar to what we did with taste modalities, but instead of ranking which citric acid solution was more sour, we did it with real coffees. I like how this exercise helps you break down a coffee&apos;s flavor into smaller components that are easier to analyze. It helps develop your ability to focus on one attribute at a time, and to remember flavor from one cup to the next, which is critical for scoring each of the ten categories on the SCA cupping form.</p><p>I think this is probably the easiest exercise to do at home. Just get a handful of coffees (maybe 2-4) and brew them up. Throw in a non-specialty coffee from the supermarket just to make the contrast starker if you can. Mix them up so that you don&apos;t know which is which, and then pick some axes to evaluate them on. Sweetness, acidity, and elements of mouthfeel like thickness and drying-ness could be fun to start with.</p><p>Next up were the triangulation exercises. Triangulation is a sensory test where there are sets of three cups. Two out of the three are the same coffee and one is different. Your job is to figure out for each set which is the odd one out. It sounds like it should be easy to tell two coffees apart. After all, the whole premise of specialty coffee is that different coffees taste different and that they are not just a fungible commodity. You can smell the difference between a non-specialty coffee and a good Gesha from a mile away, however, when you put similar coffees on the table it can get surprisingly tricky. The order in which you taste can amplify differences that exist or create ones that don&apos;t. When it&apos;s obvious, it&apos;s obvious, but when no obvious difference jumps out at you, it takes a lot of focus to zoom in on every little detail and taste things back and forth enough times to spot the difference. Especially under test conditions, this exercise is stressful.</p><p>In this exercise you usually want to analyze the coffees one axis at a time instead of trying to describe their flavor. We brewed each of these coffees using the SCA cupping protocol in the red room, so we got to see the coffee at many different stages of brewing to consider its fragrance, aroma, flavor, aftertaste, acidity, and body. At least for me, I had to make an intentional effort to take in the overall effect of the coffee to see if one axis or another would be easiest to compare. Advice from Valerian: do the easy thing. If the fragrance is clearly different, trust it. If the acidity is clearly different, trust it. If your achilles heel is describing the &quot;body&quot; and mouthfeel, try to examine every other factor first before relying on those. The more you taste the coffee, the more fatigued your palate gets, and the harder it is to spot the differences. The coffee also changes as it cools, and some differences are easier to spot at certain temperatures. You have to be vigilant about noting any differences you find right away and be confident to not second guess yourself later when the signal gets weaker.</p><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F53A;</div><div class="kg-callout-text"><strong>Me During Triangulations</strong><br><br>&quot;Are you blackberry jam? Yes. Are <em>you</em> blackberry jam? No.&quot;<br><br>&quot;Fruit. Cereal water. Fruit.&quot;<br><br>&quot;This is a little drying. This is also a little drying. I&apos;m screwed.&quot;</div></div><p>Of the exercises we did, triangulation is probably the second most applicable (after cupping) when actually working on flavor development. It&apos;s used across the industry to experimentally determine if a difference is perceptible. Roasters use it when developing blends to make sure a blend tastes consistent as the input coffees change, or to evaluate if new equipment or operators are creating consistent results. Brewers use it to evaluate whether their brewing techniques are making a difference in the cup.</p><figure class="kg-card kg-embed-card kg-card-hascaption"><iframe width="200" height="113" src="https://www.youtube.com/embed/y8kb80dvxKs?start=454&amp;feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen title="TIPS TO IMPROVE YOUR COFFEE FOR FREE"></iframe><figcaption>Here&apos;s an example of Lance Hedrick doing a triangulation to determine if removing the chaff from your coffee grounds leads to a perceptible difference in the cup. If the link doesn&apos;t automatically do it, try starting the video at around 7:34.</figcaption></figure><p>The test can work two ways: evaluating the tasters or evaluating the coffee. When you&apos;re taking a test, the coffees are known to be different and you&apos;re the one being judged. When you do this in practice, you don&apos;t know beforehand if there will be a perceptible difference between the samples, and you&apos;re judging the coffee. Note that this isn&apos;t a qualitative test: it&apos;s not about whether the odd one out tastes better or worse. It just determines if there&apos;s a perceptible difference. That difference can be a starting point to determine why they&apos;re different and which people prefer. Half the time, your goal is for there to <em>not</em> be a difference (e.g. making sure the latest version of your blend tastes the same as the previous one).</p><p>I&apos;m not sure I&apos;d recommend doing this at home unless you&apos;re practicing for the test or some other professional purpose. Specifically for the test, I think it takes some time to acclimate to the red room environment and get used to triangulating under pressure when there are a lot of people around you.</p><h3 id="week-one-epilogue-problem-student">Week One Epilogue: Problem Student</h3><div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-text">&quot;<em>You know, on the first day of class I was wondering if I should warn you that I&apos;m gonna be the problem student. I felt like I was starting way behind all my friends in terms of my ability to taste.</em>&quot;<br><br>Sitting in the lab after class on Friday, I was surprised at how much had happened in the last five days, and that there were still six more days of class to go.<br><br>Valerian walked away from the roaster and took a seat on the couch.<br><br>&quot;<em>But you&apos;re not a problem student. The only time we have a problem student is when someone is not willing to cooperate with the rest of the class or refuses to learn.</em>&quot;</div></div><p>Thus ended the first week of classes. The week was hard work, but it was fun. I&apos;d discovered that I wasn&apos;t hopeless even if I still felt like I had a ways to go in developing my abilities to taste and describe coffee. Learning was fulfilling, meeting new coffees was exciting. Being able to taste and smell intentionally added a new dimension to everyday things like taking a walk and smelling a jasmine bush from a block away or trying a mango tembleque and having the flavor just explode in my mouth.</p><p>Since we only had one day off between SCA Sensory Intermediate and the start of the Q, I took advantage of the downtime to eat food that might be too flavorful to eat during the test. My friend Arnav came over to visit, and we settled on some (mildly) spicy Burmese food for dinner and relished in the intense flavors.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://ifwego.co/content/images/2023/07/IMG_0386.jpg" class="kg-image" alt="Coffee Day 5: This Is Supposed To Be Fun" loading="lazy" width="2000" height="1500" srcset="https://ifwego.co/content/images/size/w600/2023/07/IMG_0386.jpg 600w, https://ifwego.co/content/images/size/w1000/2023/07/IMG_0386.jpg 1000w, https://ifwego.co/content/images/size/w1600/2023/07/IMG_0386.jpg 1600w, https://ifwego.co/content/images/size/w2400/2023/07/IMG_0386.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>Not being able to eat spicy food for five days meant I was gonna enjoy these curry noodles.</figcaption></figure>]]></content:encoded></item><item><title><![CDATA[[Audio] Coffee Day 4: Break Your Brain]]></title><description><![CDATA[👷 This post is under construction. It's only available to testers for now. An experimental reading of "Coffee Day 4: Break Your Brain".]]></description><link>https://ifwego.co/audio-coffee-day-4-break-your-brain/</link><guid isPermaLink="false">64a246cbf3c1740001d4656a</guid><category><![CDATA[Coffee]]></category><category><![CDATA[Listen]]></category><category><![CDATA[Coffee June]]></category><dc:creator><![CDATA[Daniel Pok]]></dc:creator><pubDate>Fri, 30 Jun 2023 07:17:00 GMT</pubDate><media:content url="https://ifwego.co/content/images/2023/07/audio-overlay.jpg" medium="image"/><content:encoded/></item><item><title><![CDATA[Coffee Day 4: Break Your Brain]]></title><description><![CDATA[<div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-text">&quot;<em>Man, I wish I could borrow your tongue</em>,&quot; one of my classmates says.<br><br>We&apos;re standing around the stone table chatting as we get ready to take a lunch break. The vial of test strips had been located, so we&apos;d gotten to lick paper that</div></div>]]></description><link>https://ifwego.co/coffee-day-4-break-your-brain/</link><guid isPermaLink="false">648bbbd2a8cccb00015ab8a3</guid><category><![CDATA[Coffee]]></category><category><![CDATA[Coffee June]]></category><dc:creator><![CDATA[Daniel Pok]]></dc:creator><pubDate>Fri, 30 Jun 2023 07:16:40 GMT</pubDate><media:content url="https://ifwego.co/content/images/2023/06/IMG_0221.jpg" medium="image"/><content:encoded><![CDATA[<div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-text">&quot;<em>Man, I wish I could borrow your tongue</em>,&quot; one of my classmates says.<br><br>We&apos;re standing around the stone table chatting as we get ready to take a lunch break. The vial of test strips had been located, so we&apos;d gotten to lick paper that morning after all. The paper has a bittering agent on it, and if you put it on your tongue for a few seconds, you can see how strongly you react to it to guess whether you have above average taste sensitivity (a &quot;super taster&quot;) or below ( a &quot;non-taster&quot;). We suspect that one of our classmates is a super taster because she&apos;s been rinsing her mouth out and seems ready to start scraping off her tongue.<br><br>The rest of us are debating whether we suck at tasting because the bitterness seems more tolerable than expected. We&apos;re rating the intensity as a 1-2 out of 5, when we thought it&apos;d be more like a 3 or 4. We decide that maybe the problem is that we&apos;d tasted too much coffee that morning and were already accustomed to the bitterness or perhaps our tongues were dried out and the bitterant just wasn&apos;t dissolving.<br><br>&quot;<em>Kids these days,</em>&quot; Valerian says as he walks up to the table, pantomiming exasperation, &quot;<em>They want everything. They&apos;re like <strong>&apos;I want your tongue. Give it to me.&apos;</strong></em>&quot;</div></div><img src="https://ifwego.co/content/images/2023/06/IMG_0221.jpg" alt="Coffee Day 4: Break Your Brain"><p>I&apos;m writing this after both weeks of classes ended, but I wanted to capture some of the moments and feelings I had from day 4 (the 2nd day of <a href="https://bootcoffee.com/sca-sensory-module-coffee-skills-program/?ref=ifwego.co">SCA Sensory Intermediate</a>) because I feel like it was a turning point where I started to understand how to walk down the path to refining my senses of taste and smell. It&apos;s a bit all over the place with bits I learned all throughout both weeks and some info from the books <a href="https://www.google.com/books/edition/Flavor_The_Science_of_Our_Most_Neglected/YUp8DAAAQBAJ?ref=ifwego.co"><em>Flavor: The Science of Our Most Neglected Sense</em></a><em> </em>by Bob Holmes<em> </em>and<em> </em><a href="https://www.fredayuan.com/product-page/sip-n-slurp?ref=ifwego.co"><em>Sip&apos;n&apos; Slurp</em></a><em> </em>by Freda Yuan<em>.</em></p><p>We can start off with some info about papillae because in my last post I promised I&apos;d cover it.</p><h2 id="it-was-day-4-the-second-day-of-sca-sensory-intermediate">It was Day 4, the second day of SCA Sensory Intermediate.</h2><p>Unless you&apos;re taking the course, you&apos;re probably not dying to know the details of papillae. The tl;dr is that they are the bumps on your tongue that contain most of your taste buds. Different kinds of papillae are more sensitive to different taste modalities and tend to group around certain areas of your tongue. It&apos;s not as clear cut as the &quot;tongue maps&quot; we saw in school imply; you&apos;re tasting all modalities in every part of your tongue (just to different degrees). </p><p>There are four main kinds:</p><ul><li><strong>Fungiform</strong>: Mostly sense sweet. Make up ~50% of your gustatory papillae. Tend to be on the tip of your tongue. Mnemonic: &quot;Fungiform sounds like fungus. Fungus grows in sugar. Sugar = sweet.&quot; Fungiform means &quot;mushroom shaped&quot;, so that&apos;s not too far off.</li><li><strong>Foliate</strong>: Mostly sense salty and sour. Make up ~25% of your gustatory papillae. Tend to be on the sides of your tongue. Mnemonic: &quot;The <em>fo-</em> in foliate kinda sounds like pho. Pho is sour and salty.&quot;</li><li> <strong>Circumvallate</strong>: Mostly sense bitterness. Make up ~25% of your gustatory papillae. Tend to be in the back of your tongue to trigger your gag reflex, since bitterness usually indicates something you shouldn&apos;t eat (e.g. poison). I don&apos;t have a mnemonic for this, but it&apos;s the only one that doesn&apos;t start with &quot;F&quot;, and humans are way more sensitive to bitterness than the other taste modalities.</li><li><strong>Filiform</strong>: These papillae don&apos;t sense a taste modality, but instead they sense tactile sensation (i.e. mouthfeel). They aren&apos;t counted as gustatory papillae (0%). Mnemonic: &quot;Filiform sounds like <em>feel-i-form</em>, and filiform detects touch.&quot;</li></ul><h3 id="an-imposter-in-coffeeland">An Imposter in Coffeeland</h3><p>Here&apos;s one of the moments that I kept jumping back to and made me wonder if I was crazy to attempt to take the Q:</p><div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-text">Eugene holds the door open as we step out of the Equator in Burlingame onto the blindingly bright pavement. I&apos;m holding a paper cup of some single-origin pour-over that&apos;s a bit too hot in one hand while juggling my laptop in the other. I take a sip. I hand it to him.<br><br>&quot;<em>Oh wow</em>,&quot; I say as we start to cross the street, &quot;<em>I&apos;m actually getting a hint of pear* from this like they wrote in the flavor notes on the sign, but it&apos;s kinda subtle.</em>&quot;<br><br>He takes a sip. &quot;<em>Huh. This is like cough syrup to me. The flavor is so intense that I couldn&apos;t drink it every day. I&apos;m getting a bit of earl grey in it.</em>&quot;<br><br>Earl Grey wasn&apos;t on the sign, and I definitely hadn&apos;t picked it out. The whole thing mostly tasted like coffee to me.<br><br>I attempt to slurp the coffee, more because it&apos;s still too hot than to be pretentious. I close my eyes and focus on tasting because at this point I&apos;m not sure if I&apos;m even tasting the pear anymore. I&apos;m trying as hard as I can to find any trace of bergamot or black tea, but I&apos;m not sure if I&apos;m actually tasting a faint hint of something or just projecting. He gives me a teasing laugh seeing how hard I&apos;m trying.<br><br>I&apos;m frustrated and a little disappointed. Without making eye contact, I murmur back, &quot;<em>Yeah, I get a little bit of that. I guess.</em>&quot;<br><br>* <em>Flavor notes have been changed to protect the identities of the innocent.</em></div></div><p>I&apos;ve always had to work harder than my friends to taste things, I just didn&apos;t realize until relatively recently that I could eat something and completely not think about how it tastes or smells. I grew up with bad allergies that meant I couldn&apos;t smell for large parts of the year. I enjoy food and I enjoy flavor &#x2013; I just didn&apos;t realize that what I was tasting wasn&apos;t quite the same as everyone else.</p><p>It wasn&apos;t until I started to get into coffee that I realized that my ability to perceive flavor was different from my friends&apos;. I helped organize events for the coffee club at work, and I put together tastings at home with my friends, but almost every time, when other people perceived a huge difference, I&apos;d only perceive a slight one.</p><p>Feeling like you&apos;re different is a great way to trigger your imposter syndrome. You can&apos;t in good conscience claim that coffee should be respected because different coffees taste different and then be unable to taste that difference consistently. Instead I found that there&apos;s a lot to hide behind in coffee: everyone perceives taste differently, different brew methods and equipment can produce very different cups from the same beans, and the perception of flavor can be heavily influenced by what you ate recently. It&apos;s pretty easy to regurgitate talking points from coffee YouTube in lofty prose without being sure that you&apos;ve experienced it yourself.</p><h3 id="flavor-isnt-really-about-taste-mostly">Flavor Isn&apos;t Really About Taste (Mostly)</h3><div class="kg-card kg-callout-card kg-callout-card-green"><div class="kg-callout-emoji">&#x1F92F;</div><div class="kg-callout-text"><strong>You &quot;taste&quot; with your nose.</strong><br><br>That is, you perceive flavors mostly through smell. &quot;Flavor&quot; is the combination of the taste and smell sensations you get when you put something in your mouth. Technically speaking, &quot;taste&quot; only refers to the basic taste modalities which you sense with your tastebuds, but we use the word &quot;taste&quot; colloquially to mean both &quot;perceive the basic taste modalities&quot; (sweet, salty, sour, bitter, umami) and &quot;experience flavor&quot; (the combination of both taste and smell).</div></div><p>One of the big &quot;aha!&quot; moments for me was grokking that &quot;flavor&quot; involves your tongue, your nose, and your brain. Flavor is a combination of what we taste (sweet, salty, sour, bitter, and umami) and what we smell. Sources disagree as to exactly how much taste and smell individually contribute to the overall experience of flavor, but smell appears to be the lion&apos;s share: if you pinch your nose and try to guess what flavor is in your mouth (e.g. from a jelly bean of unknown color), you&apos;re going to have a hard time. When you &quot;taste&quot; the flavor of a blueberry, it&apos;s your nose that gets all the floral, fruity sensations that make that berry unique. Your tongue is only sensing the basic taste modalities: the flesh is sweet and sour, and the skin is a little bitter and a little sweet or sour. Those same tastes could just as well describe any number of other berries: raspberries, blackberries, strawberries, and so on.</p><p>Our model of taste is simpler than our model of smell: we can enumerate basic taste modalities (even if there are debates on whether more exist), we have a somewhat intuitive model for how it&apos;s detected (papillae contain taste buds that are more sensitive to one modality or another), and we have substances that can evoke them in an almost &quot;pure&quot; form (e.g. sugar for sweetness, salt for saltiness, or citric acid for sourness).</p><p>By contrast, smells are more complicated: we don&apos;t have basic building blocks for smells or named receptors for them. Smell is more innate and primal in a way: the olfactory epithelium in our nose contains receptors for (we think) 400 or so odor molecules. These are wired into the olfactory bulb, a disco ball of sensory neurons that helps us interpret smells and plugs directly into the limbic system, which processes emotions and memories, bypassing the cerebral cortex. We name smells by comparing them to what we&apos;ve already smelled, basically by pattern matching. We&apos;re taking readings off of a chemical analyzer (our olfactory bulb) and trying to fit our readings to a model of what we&apos;ve encountered before in the world.</p><p>It&apos;s unstructured, kinda like a Rorschach test or cloud gazing, and it&apos;s hard to consciously think about. Usually, when we smell something we recognize, it triggers a snap association in our brains without us having to think too hard about it. But when we smell something like coffee, which can have a bouquet of interesting chemical signatures that don&apos;t cleanly match our model of any specific thing we&apos;ve encountered (except, maybe, &quot;it tastes like coffee&quot;), we&apos;re left with something amorphous and hard to describe. If it&apos;s hard to describe, it&apos;s hard to remember, and if it&apos;s hard to remember, it&apos;s hard to compare from cup to cup or from day to day. So it can be frustrating and hard work to decipher if a coffee tastes like blueberries because it probably doesn&apos;t contain all of the markers that signal &quot;blueberries&quot; in our memory.</p><p>If we compare flavor notes to musical notes, it&apos;s like my memory of blueberries is a complex, jazzy chord with four or five notes stacked on top of each other, each representing a different odor sensation: e.g. a high grassy note, a tense floral note, a low berry tone, etc. A coffee that tastes like blueberries is like a moving melody that hits a couple distinctive notes in that chord, but not all of them. Analyzing music requires an understanding of music theory, a memory of chords and shapes, and then a lot of guessing-and-checking to come up with a hypothesis for what chord a handful of notes belongs to. Even then, it&apos;s often unclear and open to interpretation, both in music and in flavor.</p><p>All of this makes &quot;Does this smell like blueberries?&quot; a much harder question to answer than &quot;Is this sweet?&quot;. It&apos;s a much more cerebral process with an unclear answer, and you have to arrive at a conclusion before the aroma fades.</p><p>In a way, this is also freeing: if a bag of coffee has &quot;blueberry&quot; written as a flavor note on it, it doesn&apos;t mean that it will smell or taste like a bucket of fresh blueberries. It only means that the particular bouquet of aromas and tastes in that coffee created the impression of blueberries in the mind of whoever wrote that flavor descriptor. It may very well create that impression in your mind as well when you drink it, but it very well might not, and that&apos;s totally fine too. Whether you get blueberries, a different fruit, or no fruit at all depends on your memory of those foods and the way your flavor sensing system interacts with that coffee.</p><h3 id="more-structured-ways-to-smell">More Structured Ways to Smell</h3><p>So what was I doing when I was actively trying to look for a flavor note in the coffee outside Equator with Eugene? Turns out that all that slurping, swishing, and concentrating are pretty useful ways to actively smell.</p><div class="kg-card kg-callout-card kg-callout-card-green"><div class="kg-callout-emoji">&#x1F92F;</div><div class="kg-callout-text"><strong>You smell your food twice.</strong><br><br>Our sense of smell works in two directions (inhaling/exhaling), which both contribute different data about what we&apos;re ingesting.</div></div><p>Air goes two ways through your nose: you can breathe in, and you can breathe out. (Unless you&apos;re me in elementary school, in which case you can&apos;t do either of those things most of the year. Thanks, hay fever!) As air passes through your nose in either direction, you perceive smells. However, the smells you perceive breathing in are not the same as the ones you perceive breathing out. When you breathe in or actively sniff something, we call that &quot;<em><strong>orthonasal olfaction</strong></em>&quot;. However, when you have something in your mouth (say, a strawberry or a sip of coffee) you perceive its aromas as you breathe out. We call this &quot;<em><strong>retronasal olfaction</strong></em>&quot; because the air flows in from the back of your nose. It&apos;s a big part of how both flavor and aftertaste work.</p><p>The <em>Flavor</em> book explains that these two forms of smelling work differently in two big ways:</p><ol><li><strong>What we&apos;re smelling is different. </strong>We smell the &quot;outside world&quot; with orthonasal olfaction (e.g. sniffing or breathing in), while we smell what&apos;s inside our mouths with retronasal olfaction (breathing out, aftertaste). When we breathe in, the current of air going in acts as a curtain, keeping the odor particles from inside our mouths from reaching the olfactory epithelium in our noses. This lets us get an accurate measurement of our environment or evaluate something that we&apos;re about to put in our mouths. When we breathe out, it works the opposite way: we&apos;re sending odor particles from inside our mouths up and onto the olfactory epithelium from the back.</li><li><strong>Which smells we&apos;re sensitive to is different.</strong> The receptors for the 400+ different odor molecules we can perceive are not uniformly spaced out in our nose. It&apos;s a bit less clear how they&apos;re arranged (even compared to the already-inaccurate tongue map), but we think that some sensors tend to group in the front or back of our olfactory epithelium (and I&apos;d bet that like the arrangement of papillae on the tongue, it&apos;s different for each individual). This acts as a filter over what we smell, sorta like how in audio you can have a high-pass filter (which ignores all the low frequencies) or low-pass filter (which ignores the high frequencies). Mechanically, this is because odor particles need to physically reach their matching receptor cells to contribute to the smell, which means they have to stay airborne long enough to reach them. Many molecules are heavy and will get caught in our mucosal lining before reaching their receptor, which filters them out of the smell. If a type of receptor is in the front, then we might get more signal from it during orthonasal breathing where the air comes in from the front, and less signal from it during retronasal breathing where the air comes in from the back and the molecules have a greater chance of being trapped before they can activate the receptor. That&apos;s probably one of the many reasons why coffee grounds smell very different from the flavor of the final drink.</li></ol><p>When I slurped the coffee, it aerated the fluid, helping more volatile odor molecules to fly free of the liquid and get suspended in the air, where it could flow over my olfactory epithelium during retronasal breathing and get detected as flavor. It also ensured that the coffee fluid got all over my tongue so that each of the different types of papillae had a chance to measure how sweet, salty, sour, bitter, or umami it was. It&apos;s a similar story for swishing, gargling, holding the fluid in my mouth for a bit before swallowing, and doing that lip-smacking thing where you tap your tongue to the back of your teeth (and alveolar ridge). When I focused on searching for the flavor, I breathed more slowly and evenly, which might have helped optimize how far the odor molecules made it on my olfactory epithelium in both orthonasal and retronasal olfaction.</p><p>It&apos;s clear that I looked pretty silly (and maybe obnoxious) trying to enhance my experience of the flavor, but that&apos;s what it takes for me to maximize the opportunity for my senses to capture and analyze as much of the flavor as I could.</p><p>Maybe it&apos;s fair for Eugene to laugh at all this. That&apos;s a lot of work, just to drink coffee.</p><h3 id="theory-meet-practice">Theory, Meet Practice.</h3><p>We did a few practice cuppings to get us used to the procedure of setting up and conducting a cupping. This also kicked off the process of getting calibrated in the way we assessed and scored the coffee, a process that would continue through the next week for those of us sticking around for week two to take the Q. The highest and lowest rated coffees quickly made themselves known; when you put that many coffees next to each other side by side, the differences stand out.</p><p>The best thing on the table was <a href="https://lamulacoffee.com/about-us-3/?ref=ifwego.co">La Mula</a>, a Panama Gesha developed, grown, processed, and roasted by Willem Boot, the leader of the coffee lab (his name is on the building) and our instructor for the Q. The sweetness and floral notes were bursting out of the cup from the fragrance of the ground coffee to the aftertaste of the brewed coffee. I was floored by how much flavor I was tasting consistently in every slurp and surprised at how quickly words, <em>opinionated</em> words, jumped to mind: the citrus-like acidity reminded me of yuzu, the body and tangy sweetness of Yakult, and the lingering finish like a milky oolong tea.</p><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://lamulacoffee.com/shop/our-farms/panama-finca-la-mula/la-mula-gesha-lot-25/?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">La Mula Gesha Lot 25 &#x2013; La Mula Coffee</div><div class="kg-bookmark-description">Sweet citrus acidity, aromatics of lemon, orange blossom, but also berries like blueberry. This coffee is shade grown under the canopy of local native trees in the tropical cloud forest of Panama. ORIGIN: Panama FARM: Finca La Mula ELEVATION: 5,906 feet (1800 meters) PROCESS: Natural</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://lamula.b-cdn.net/wp-content/uploads/2022/01/cropped-la-mula-only-donkey-270x270.png" alt="Coffee Day 4: Break Your Brain"><span class="kg-bookmark-author">La Mula Coffee</span><span class="kg-bookmark-publisher">Name&#xA0;*</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://lamulacoffee.com/wp-content/uploads/2021/08/la-mula-2-coffee.jpg" alt="Coffee Day 4: Break Your Brain"></div></a><figcaption>I don&apos;t think we cupped this lot specifically, but the description and point range seem close. Coming in at $100 for 340g, this coffee was grown and prepared with a meticulous dedication to quality, and it shows in both the price and the experience in the cup. If you&apos;re in a fortunate enough position to try it, I strongly recommend that you do.</figcaption></figure><p>The other thing that was surprising was how quickly the class started to converge on a set of scores. There were some inconsistencies between different people, and I had one or two samples that I rated way too high or too low due to scoring issues, but after we discussed each coffee, it was surprising how many of us found similar flavors using slightly different descriptors, and how our cumulative scores could be similar despite different ratings for each individual attribute.</p><p>Over the course of two or three cuppings, almost everyone was binning each coffee into similar ranges. Even if there was a couple point spread, we were consistently putting the highest grade coffees in the high-specialty range (around 87+), and the lowest grade coffees in the non-specialty range (around 80 points, which is the cutoff for specialty grade).</p><p>Armed with the theoretical knowledge of how flavor works, I started to make adjustments to the way I tasted and smelled: </p><ul><li>I discovered that when my brain wasn&apos;t awake, my nose wasn&apos;t awake. I needed to eat a bit and drink some coffee to wake myself up before I could start analyzing coffee. Before I&apos;m warmed up, everything just tastes like coffee.</li><li>I realized that my glasses were sinking down on my face over time and when they got too low, they&apos;d pinch my nose, making it harder to smell. I started to clean my glasses more or even take them off when I was going to smell a lot of samples in a row.</li><li>I figured out how close I needed to get to the cupping bowls to smell the fragrance and aroma of the coffee (turns out: pretty close). I also discovered a trick where sniffing while moving my nose slowly back and forth above the lip of the bowl would create some contrast and help me find the spot where the fragrance was strongest.</li><li>I settled on a breathing exercise where I&apos;d breathe in slowly through my nose at around the rate that helped me smell the best and then breathe out slowly, first through my mouth, then through my nose. I think it was one part mental preparation, one part practicing the motion of smelling, and one part priming my body to open my airways.</li><li>I picked a few different ways to move the coffee liquid around in my mouth to aerate the aromatics and feel the texture. My slurping got a bit more confident too &#x2013; there&apos;s a bit of aim involved to get it all over your tongue without getting it into your throat, which will make you choke.</li><li>I got better at knowing when my sense of taste was saturated and how to take a break and rinse out my mouth. Similarly, I picked up a trick whereby I would sniff my clothes when my sense of smell was getting saturated, the theory being that your own scent is neutral.</li></ul><p>With all that in place, I was starting to feel more consistent, and since my inconsistency was my greatest fear going into the class, I was finally starting to feel more confident. After a couple sessions I thought, <em>Ok, maybe I can actually do this.</em></p><h3 id="break-your-brain">Break Your Brain</h3><div class="kg-card kg-callout-card kg-callout-card-green"><div class="kg-callout-emoji">&#x1F92F;</div><div class="kg-callout-text"><strong>Perceiving and naming flavors is something that can be trained and practiced.</strong><br><br>In other words: there&apos;s still hope for me.</div></div><p>A week of nonstop coffee drinking and coffee talk will fry your brain in addition to your palate, but sitting in the lab after class on day four, as I watched Valerian roast coffee for the next few days of classes, I realized that I had experienced a deeper paradigm shift.</p><p>It reminded me of when I saw people learn computer programming for the first time. On the surface, writing code looks similar to the way we humans teach and instruct each other to do things: <em>if this is true, then do that</em>. We talk this way all the time. But when you start to write code, you realize that computers work in a much more rigid, much more <em>literal</em> way. They don&apos;t assume things because they don&apos;t have a model of the world besides what some programmer has explicitly spelled out for them. They can&apos;t generalize, and they don&apos;t handle edge cases that are &quot;close enough&quot;, because they don&apos;t know what &quot;close enough&quot; means until you tell them. (Machine learning and LLMs like ChatGPT are changing some of these assumptions, but let&apos;s set that aside for now.)</p><p>Many of the first-time learners I&apos;ve seen go through this journey where at first it seems to make sense (<em>&quot;If the number is even, then do this thing.&quot;</em>), then they hit the point where the abstraction breaks down and they need to express concepts in the computer&apos;s terms instead of in the human terms they&apos;re used to (<em>&quot;What do you mean I have to define what &apos;even&apos; means? Isn&apos;t that obvious?&quot;</em>). Then, they struggle for a bit as they learn the structure of how a computer &quot;thinks&quot; and the basic building blocks they have to express their logic (<em>&quot;An even number is a number that can be divided by two with no remainder. We can use the modulus operator to do this test.&quot;</em>). Finally, there&apos;s a moment when something shifts and you can see that they&apos;ve internalized the structure and limitations of computer programs, and they&apos;re no longer trying to bend the syntax of a programming language to match their human experience of reality, but engaging the computer on its own terms.</p><p>It&apos;s the moment where they go from &quot;this makes no sense&quot; to &quot;this is hard, but I can do this&quot;. It&apos;s a hopeful and intimidating moment: like you can hear the machinery inside their head shear, rotate, tumble a bit, and finally settle somewhere a bit lower to the ground but much more stable than the cliff they were holding on to. It&apos;s both the moment where they realize &quot;my experience instructing other humans isn&apos;t going to help me program computers&quot;, but also the moment when they realize the monumental power of what they&apos;ve just unlocked: even though the primitives are simple and it feels like you&apos;re sitting on the ground playing with Lincoln Logs, you truly believe that you will be able to apply powerful concepts to these primitive building blocks over and over again until you&apos;ve built a skyscraper.</p><p>That&apos;s how I felt after I started to internalize what I was learning about flavor. Generally speaking, it&apos;s not a matter of hardware: to paraphrase the <em>Flavor</em> book, if you can taste the difference between a blueberry and a raspberry, you have what it takes to taste the notes in coffee. It just takes practice and an awareness of what to look for and how to build your memory of aromas. Some people might do this innately, but others (like me) need some coaching and coaxing to start down this path. It requires familiarizing yourself with your instruments and discovering how your senses respond to different tastes and smells. It helps to explore how the way you experience something differs from how someone else experiences it. Sometimes, you have to commit to looking ridiculous and putting your nose to your plate of food or some fresh fruit in the produce aisle. For me, it means being conscientious of what conditions my body needs to taste and smell, and being intentional about stopping to taste and smell everything I encounter&#x2013;coffee or otherwise.</p><h3 id="further-reading">Further Reading</h3><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.google.com/books/edition/Flavor_The_Science_of_Our_Most_Neglected/YUp8DAAAQBAJ?hl=en&amp;gbpv=1&amp;printsec=frontcover&amp;ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Flavor: The Science of Our Most Neglected Sense</div><div class="kg-bookmark-description">A journey into the surprising science behind our flavor senses. Can you describe how the flavor of halibut differs from that of red snapper? How the taste of a Fuji apple differs from a Spartan? For most of us, this is a difficult task: flavor remains a vague, undeveloped concept that we don&#x2019;t know&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.google.com/favicon.ico" alt="Coffee Day 4: Break Your Brain"><span class="kg-bookmark-author">Google Books</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://books.google.com/books/publisher/content?id=YUp8DAAAQBAJ&amp;printsec=frontcover&amp;img=1&amp;zoom=1&amp;edge=curl&amp;imgtk=AFLRE72xlftdOxAy_0MtCbhvktZOxVptE82_zG9PDFUEbOlziVyz7nwyQtucCFkFSGNV0gI_dIpx7gr2tGUMBs_-x9DNvWAkflHbM5TM2ahEn3X8Z7Aij78ARHv8pyjLVLd2pQgVDAQb" alt="Coffee Day 4: Break Your Brain"></div></a></figure><p><em>Flavor: The Science of Our Most Neglected Sense</em> by Bob Holmes, has a bunch more info on how we perceive flavor. Valerian recommended it in class, and there&apos;s a bunch of the cool tidbits in here (e.g. did you know that you have taste buds in your lungs and intestines?).</p><p>I consumed it as an audiobook, and it was a fun listen. The content in it is about flavor generally, and he tells stories about coffee, wine, whisky, tomatoes, hot peppers, and other foods to illuminate his (rather scientific) exploration into flavor. Coming out of the book, I&apos;m even more convinced that flavor is something we invent in our brains. He lays out some of the concepts I mentioned in this post early on in much greater technical detail, but then goes on to explore how other factors besides smell, taste, and mouthfeel can affect our perception of flavor. Things like placebos and expectations (e.g. people don&apos;t show a preference for organic produce in a blind taste test, but may genuinely perceive it to taste better when they know that it&apos;s organic), how aromatics can trick our brain into thinking something is sweeter than it actually is, and so on. Definitely a fun book to nerd out on.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.fredayuan.com/product-page/sip-n-slurp?ref=ifwego.co"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Sip&#x2019;n&#x2019; Slurp: A Guide to Expert Coffee Tasting | Freda Yuan Offical</div><div class="kg-bookmark-description">About Sip &#x2018;n&#x2019; Slurp: A Guide to Coffee Expert Tasting Sip &#x2018;n&#x2019; Slurp is the first self-published coffee sensory book by Freda Yuan. This book provides unique insight into using tasting coffee to embrace life and find a sense of joy in the pleasure of drinking coffee. Inspired by the author&#x2019;s own per&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://static.parastorage.com/client/pfavico.ico" alt="Coffee Day 4: Break Your Brain"><span class="kg-bookmark-author">Freda Yuan Offical</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://static.wixstatic.com/media/8e1fa4_9c532104770043218b4bc7628836859e~mv2.jpeg/v1/fit/w_500,h_500,q_90/file.jpg" alt="Coffee Day 4: Break Your Brain"></div></a></figure><p><em>Sip&apos;n&apos; Slurp</em> by Freda Yuan, is about how to taste coffee. It briefly covers her journey of how she immigrated from Taiwan to London, dealt with her insecurities and mental health issues, and became a competition winning cupper (professional coffee taster). The majority of the book is about how to perceive flavor generally. A lot of what she said lined up with what I learned in SCA Sensory as well as what was said in the <em>Flavor</em> book, but she presents it in a concise way accompanied by very stylish photos that look straight out of Instagram. She also includes some exercises for familiarizing yourself with your sensory instruments, examples of words you can use to describe a coffee&apos;s flavor and mouthfeel, and some techniques she uses to visualize a coffee when she tastes it. It&apos;s a quick read and clearly a very personal project, and I think it&apos;s worth picking up.</p>]]></content:encoded></item><item><title><![CDATA[Coffee Day 3: Resting Cupping Face]]></title><description><![CDATA[<div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-text">Our class has ten people today, more or less. There are the four folks who are in for the Q from yesterday, with two new Q folks joining. The three folks who are here for the week continue on, with one more joining. Every day the round table in the</div></div>]]></description><link>https://ifwego.co/coffee-day-3-resting-cupper-face/</link><guid isPermaLink="false">648a73faa8cccb00015ab6a9</guid><category><![CDATA[Coffee]]></category><category><![CDATA[Coffee June]]></category><dc:creator><![CDATA[Daniel Pok]]></dc:creator><pubDate>Wed, 14 Jun 2023 06:33:00 GMT</pubDate><media:content url="https://ifwego.co/content/images/2023/06/IMG_0206.jpg" medium="image"/><content:encoded><![CDATA[<div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-text">Our class has ten people today, more or less. There are the four folks who are in for the Q from yesterday, with two new Q folks joining. The three folks who are here for the week continue on, with one more joining. Every day the round table in the middle of the room gets fuller and fuller and people spill out every farther to find seats in other parts of the lab.<br><br>&quot;<em>Coffee Scum. That&apos;s us.</em>&quot; I say as we&apos;re setting up for our first full-sized cupping. I&apos;m thinking about the all the steps in the process of cupping: the synchronized pouring, the breaking and cleaning, and then the part where we walk around in a circle, taking turns poking our noses at the rows of white bowls dosing out small sips of coffee into cups and then walking away to hide from everyone else and slurp meditatively in a corner, and how odd it must look to someone who&apos;s never participated in one. I&apos;m thinking also of Mr. Giovanni Gaggia, who in 1938 brilliantly renamed the oily layer of &quot;coffee scum&quot; that appeared on the product of his newly invented espresso machine, &quot;crema caff&#xE8;&quot; and changed how the world perceived coffee forever.<br><br>As we&apos;ve learned in the course, sensory analysis is a scientific discipline. The world of coffee on the other hand is full of exacting rituals and apocryphal stories.<br><br>Valerian instructs us as he shuffles from station to station supervising the setup, &quot;<em>You must have a poker face when you&apos;re cupping. You can&apos;t make sounds like &apos;ooh&apos; or &apos;ahh&apos; or make a face or you&apos;ll bias the other cuppers. If you do it during the Q, you&apos;ll have to leave.</em>&quot;<br><br>I walk over to a couple of the other Q-hopefuls and we pantomime practicing our poker faces: bouncing between straight faces and smiles that are half faked, half from the giddy anticipation of evaluating interesting coffees while we ourselves are being scrutinized. I try to dial in my poker face. I&apos;m bouncing between stern and neutral, trying to look like I&apos;m taking it seriously without coming across too pretentious. I&apos;m definitely overthinking it.<br><br>It&apos;s almost time to begin. We&apos;re about to circle back up to start the cupping. I try to summarize the expression we&apos;ve been practicing as we prepare to focus our senses solely on the qualities of the cups before us:<br><br>&quot;<em>It&apos;s my resting cupping face.</em>&quot;</div></div><h2 id="its-wednesday-the-first-day-of-the-sca-sensory-intermediate-course">It&apos;s Wednesday, the first day of the SCA Sensory Intermediate course.</h2><img src="https://ifwego.co/content/images/2023/06/IMG_0206.jpg" alt="Coffee Day 3: Resting Cupping Face"><p>Let&apos;s talk about papillae some other time because today it&apos;s getting too late for me to properly review them and do a writeup. I was promised I&apos;d get to lick paper tomorrow to see how bad it tastes, and somehow I&apos;m looking forward to that. Even though our class today was the first day of the <a href="https://bootcoffee.com/sca-sensory-module-coffee-skills-program/?ref=ifwego.co">SCA Sensory Intermediate</a> module, it felt like a continuation of the things we started learning yesterday in SCA Sensory Foundations. Yesterday, we examined how to use the SCA flavor wheel as an aide for describing flavors, and today we spent most of the lecture segment talking about how to score coffees on the SCA cupping form.</p><h3 id="a-bit-more-about-sensory-analysis">A Bit More About Sensory Analysis</h3><blockquote>If you&apos;re not calibrated, you won&apos;t provide objective measurements.</blockquote><p>There are four steps:</p><ol><li><strong>Evoke</strong>: design experiments to best evaluate a sensory experience</li><li><strong>Measure</strong>: Collect data, e.g. using a scoring sheet or digital tools like <a href="https://www.tastify.com/?ref=ifwego.co">Tastify</a></li><li><strong>Analyze</strong>: Use statistical methods to analyze the results</li><li><strong>Interpret</strong>: Understand your results in the context of decisions to be made</li></ol><h3 id="review-what-is-cupping">Review: What Is Cupping?</h3><p>Cupping is a procedure for evaluating the attributes of a coffee while controlling as many variables as possible. This is where sensory analysis meets practical applications in the production, roasting, and brewing of coffee. The form of cupping we use in this class (and for the Q) follows the SCA Cupping Protocol, which is designed to evaluate the potential of a sample of green coffee by keeping the roasting and brewing methods constant to reduce their impact. It&apos;s a very prescriptive protocol that&apos;s meant to give each coffee a fair shot at evaluation since the results of cupping determine a coffee&apos;s quality score and, indirectly, price.</p><p>You can check out the <a href="https://sca.coffee/research/protocols-best-practices?ref=ifwego.co">SCA&apos;s website</a> for the exact details, or you can watch some of these YouTube videos to get the gist of it:</p><figure class="kg-card kg-embed-card kg-card-hascaption"><iframe width="200" height="113" src="https://www.youtube.com/embed/_94H5-G3LDc?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen title="Cupping coffee with the SCA form"></iframe><figcaption>Trish, from San Francisco&apos;s Wrecking Ball Coffee Roasters: one of the Bay Area&apos;s coffee luminaries and credited with coining the term &quot;Third Wave Coffee&quot;.</figcaption></figure><figure class="kg-card kg-embed-card kg-card-hascaption"><iframe width="200" height="113" src="https://www.youtube.com/embed/cSEgP4VNynQ?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen title="How To Cup (Taste) Coffee At Home"></iframe><figcaption>James Hoffman, a weird coffee person. (Same video from the coffee Day 0 post)</figcaption></figure><figure class="kg-card kg-embed-card kg-card-hascaption"><iframe width="200" height="113" src="https://www.youtube.com/embed/8B6QX-UvsvY?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen title="How I Approach Tasting Coffee"></iframe><figcaption>Morgan Eckroth of Morgan Drinks Coffee</figcaption></figure><figure class="kg-card kg-embed-card kg-card-hascaption"><iframe width="200" height="113" src="https://www.youtube.com/embed/9kEOsX3yTHE?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen title="HOW TO CUP AT HOME: The What, Why, and How of Cupping!"></iframe><figcaption>Lance Hedrick, of many things but most notably Onyx Coffee Lab.</figcaption></figure><h3 id="what-cupping-is-and-is-not">What Cupping Is And Is Not</h3><div class="kg-card kg-callout-card kg-callout-card-green"><div class="kg-callout-emoji">&#x2705;</div><div class="kg-callout-text"><strong>A sensory analysis process specific to the evaluation of coffee.</strong><br><br><strong>Two things to note here:</strong><br>1. It&apos;s analytical, not necessarily indicative of preference.<br>2. It&apos;s specific to coffee (though similar processes may exist for evaluating other things, like wine).</div></div><div class="kg-card kg-callout-card kg-callout-card-red"><div class="kg-callout-emoji">&#x274C;</div><div class="kg-callout-text"><strong>A method to evaluate customers&apos; preferences. </strong><br><br>End customers are not trained or calibrated as cuppers. While they can certainly taste coffees and offer their evaluations, they don&apos;t know the technical language and standards of cupping, and their scores will be more subjective and less objective compared to trained cuppers whose scores are calibrated with each other.</div></div><div class="kg-card kg-callout-card kg-callout-card-red"><div class="kg-callout-emoji">&#x274C;</div><div class="kg-callout-text"><strong>A Way to Identify Foreign Odors</strong><br><br>The procedure for cupping requires an environment free of interfering odors (or any other distracting stimuli, like noise) that could affect the results.</div></div><div class="kg-card kg-callout-card kg-callout-card-red"><div class="kg-callout-emoji">&#x274C;</div><div class="kg-callout-text"><strong>Sensory Analysis That Only Evaluates The Cleanliness of Coffee </strong><br><br>Evaluating cleanliness is an important aspect of cupping, but there&apos;s a lot more to it, especially for high-quality speciality coffees that ought to have good quality control to begin with.</div></div><p>The course highlights the important role that cupping has for buyers of green coffee specifically:</p><ul><li>It was originally created solely to detect defects in shipments of coffee, but has expanded to include identifying and describing positive qualities of a coffee.</li><li>It facilitates communications between buyers and sellers about the quality and value of a lot of coffee.</li><li>It allows buyers to evaluate the coffee they receive against contract agreements and mitigate rejections.</li></ul><h3 id="the-setup-for-cupping">The Setup for Cupping</h3><p>When cupping coffees, we need to assess both quality and uniformity. Therefore, we need to taste multiple examples of the same coffee in order to evaluate whether it is consistent and free of defects. In order to do this, we brew each coffee (called a &quot;sample&quot;) five times in parallel. Each of these is one cup, and there are five cups per sample (which, again, is one coffee). If multiple coffees are being cupped, then there are five times as many cups of brewed coffee being evaluated. For example, if there are six coffees being cupped, that means we have six &quot;samples&quot;. Each sample has five cups, so we will brew and evaluate 30 cups of coffee total. As you can imagine, you have to be pretty fast to do this before the coffee cools down and its flavor changes.</p><p>It can be handy to put all five cups in a sample on a tray with labels designating each cup from 1 to 5. This is important because if a sample contains a defect, the cuppers need to note which specific cups they found the defect in.</p><ul><li>Everyone shares all of the trays. They are not individual.</li><li>It&apos;s not 1 cup per person. Everyone must try every cup.</li><li>All of the cups on the tray (e.g. in the sample) are of the same coffee. They are supposed to taste identical, but they might not if there are uniformity issues.</li></ul><p>This tripped me up at first, and I noticed it confused some folks in the class too. Here&apos;s an infographic showing how the 5 cups in a sample are arranged in an &quot;M&quot; shaped formation.</p><figure class="kg-card kg-image-card"><img src="https://ifwego.co/content/images/2023/06/Cupping-Tray-With-Labels.png" class="kg-image" alt="Coffee Day 3: Resting Cupping Face" loading="lazy" width="1350" height="1080" srcset="https://ifwego.co/content/images/size/w600/2023/06/Cupping-Tray-With-Labels.png 600w, https://ifwego.co/content/images/size/w1000/2023/06/Cupping-Tray-With-Labels.png 1000w, https://ifwego.co/content/images/2023/06/Cupping-Tray-With-Labels.png 1350w" sizes="(min-width: 720px) 720px"></figure><p>Here&apos;s a wider shot of the cupping table from the image at the top of this post. There are six samples on the table. Each sample is on a tray that&apos;s arranged like the graphic above.</p><figure class="kg-card kg-image-card"><img src="https://ifwego.co/content/images/2023/06/IMG_0204.jpg" class="kg-image" alt="Coffee Day 3: Resting Cupping Face" loading="lazy" width="2000" height="1500" srcset="https://ifwego.co/content/images/size/w600/2023/06/IMG_0204.jpg 600w, https://ifwego.co/content/images/size/w1000/2023/06/IMG_0204.jpg 1000w, https://ifwego.co/content/images/size/w1600/2023/06/IMG_0204.jpg 1600w, https://ifwego.co/content/images/size/w2400/2023/06/IMG_0204.jpg 2400w" sizes="(min-width: 720px) 720px"></figure><h3 id="the-results-of-cupping-other-than-the-score">The Results of Cupping (Other Than The Score)</h3><div class="kg-card kg-toggle-card" data-kg-toggle-state="close"><div class="kg-toggle-heading"><h4 class="kg-toggle-heading-text">Clear, Comprehensive Descriptors</h4><button class="kg-toggle-card-icon"><svg id="Regular" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path class="cls-1" d="M23.25,7.311,12.53,18.03a.749.749,0,0,1-1.06,0L.75,7.311"/></svg></button></div><div class="kg-toggle-content"><p>Descriptors are a tool for cuppers to communicate with each other, with other professionals who are interested in the coffee, and sometimes with consumers who are unfamiliar with industry lexicon. Descriptors that are specific and evocative to a specific cupper may help them remember a certain cup and capture their feelings about it, but might not be useful to others who have never experienced the aroma being referenced. Doubly so if the descriptor doesn&apos;t refer to an aroma, but to a memory specific that person experienced (e.g. &quot;my grandma&apos;s gingerbread cookies right as they come out of the oven&quot;).</p><p>I think that both specific and accessible descriptors are important, and each has its place, but we should keep our audience in mind when we decide what to write on a cupping form or bag of coffee. There&apos;s clearly an art to figuring how to describe a coffee in a way that connects with a particular audience based on their technical sophistication and the palette of flavors they experience. Just in our class today we had people with connections to North, South, and Central America, Eastern Europe, Western Europe, East Asia and India. Specific flavors and foods from all of those places have come up repeatedly, and sharing them all with each other can paint a pretty vivid picture of a complex taste experience.</p></div></div><div class="kg-card kg-toggle-card" data-kg-toggle-state="close"><div class="kg-toggle-heading"><h4 class="kg-toggle-heading-text">Thorough Analysis of Acidity, Body, Balance, and Sweetness</h4><button class="kg-toggle-card-icon"><svg id="Regular" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path class="cls-1" d="M23.25,7.311,12.53,18.03a.749.749,0,0,1-1.06,0L.75,7.311"/></svg></button></div><div class="kg-toggle-content"><p>Specialty-grade Arabica coffees are required to have a certain level of perceived sweetness, and the other aspects listed (acidity, body, and balance) are specific attributes that are judged on the SCA cupping form to reward coffees that exhibit positive qualities.</p></div></div><div class="kg-card kg-toggle-card" data-kg-toggle-state="close"><div class="kg-toggle-heading"><h4 class="kg-toggle-heading-text">Acknowledgement and Identification of Defects</h4><button class="kg-toggle-card-icon"><svg id="Regular" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path class="cls-1" d="M23.25,7.311,12.53,18.03a.749.749,0,0,1-1.06,0L.75,7.311"/></svg></button></div><div class="kg-toggle-content"><p>There are three &quot;technical&quot; categories on the form which represent aspects of the coffee other than how it&apos;s experienced when you drink it. These aspects are closer to an evaluation of how the coffee performs as a commodity:</p><ol><li><strong>Uniformity</strong>: Do multiple cups in a sample taste the same, or are there different characteristics or defects in some of them?</li><li><strong>Clean Cup</strong>: Are there any issues in the cup (taints or faults) that make the coffee taste off or indicate issues in the processing or storage of the coffee?</li><li><strong>Sweetness</strong>: Does the coffee contain the required level of sweetness characteristic of a specialty-grade arabica (equivalent to 5g/L of sugar)? This is related to &quot;Clean Cup&quot; since cups without issues generally contain sweetness. In most specialty coffees, things need to go pretty wrong for a cup to not exhibit that minimum bar of sweetness.</li></ol><p>In these categories, a coffee starts off with full points (10) and 2 points are subtracted for each cup that fails to meet the standard. If a cup is marked down for not being a &quot;clean cup&quot;, that indicates that a defect (taint or fault) is present, and the defect must be named.</p><p>Having a cup fail one of these categories often carries compounding penalties For example, if a cup fails &quot;clean cup&quot;, it fails &quot;uniformity&quot; as well, and it&apos;ll lose at least 2 additional points for whatever defect caused it to fail. It&apos;s important to get these right because wrongly marking down a cup for a defect could drastically penalize that coffee and unfairly punish coffee producers.</p></div></div><h3 id="the-scores">The Scores</h3><ul><li>Coffee is scored on a 100 point scale.</li><li>The bar for specialty coffee is <strong>80+ points</strong>.</li><li>A coffee&apos;s score only counts if it&apos;s determined by at least 2 independent Q graders.</li></ul><p>There are ten categories, each worth ten points. They are broken down as follows:</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th><strong>Quality Categories</strong></th>
<th>Gets points for the quality of...</th>
<th>70 pts Total</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Fragrance/Aroma</strong></td>
<td>Fragrance is smell of the dry grounds; aroma is the smell of the brewed coffee.</td>
<td>10 pts</td>
</tr>
<tr>
<td><strong>Flavor</strong></td>
<td>The aroma and taste in your mouth.</td>
<td>10 pts</td>
</tr>
<tr>
<td><strong>Aftertaste</strong></td>
<td>The lingering flavors after swallowing or spitting.</td>
<td>10 pts</td>
</tr>
<tr>
<td><strong>Acidity</strong></td>
<td>The favorability of the acidity in the coffee.</td>
<td>10 pts</td>
</tr>
<tr>
<td><strong>Body</strong></td>
<td>The texture and mouthfeel.</td>
<td>10 pts</td>
</tr>
<tr>
<td><strong>Balance</strong></td>
<td>How well the flavor, aftertaste, acidity, and aroma work together.</td>
<td>10 pts</td>
</tr>
<tr>
<td><strong>Overall</strong></td>
<td>The cupper&apos;s overall impression of the coffee.</td>
<td>10 pts</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th><strong>Technical Categories</strong></th>
<th>Starts with full points and loses points if...</th>
<th>30 pts Total</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Uniformity</strong></td>
<td>A cup tastes different (usually worse) than the others.</td>
<td>10 pts</td>
</tr>
<tr>
<td><strong>Clean Cup</strong></td>
<td>Something tastes defective in the cup.</td>
<td>10 pts</td>
</tr>
<tr>
<td><strong>Sweetness</strong></td>
<td>The cup is so impacted by a defect that no sweetness is detectable.</td>
<td>10 pts</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>An 80 point coffee might be inoffensive (it &quot;tastes like coffee&quot;) but lack unique flavors that make it more special than most other coffees. It might be improved with some cream and sugar. It&apos;s probably not too memorable. Its main flavors are probably those most common to coffee: chocolate, caramel, nutty, etc.</p><p>An 84 point coffee has a story to tell. It&apos;s the kind of coffee that has &quot;notes&quot;, and you probably won&apos;t have to work that hard to find them. Maybe it has a bit of sweetness and dark fruit notes. Maybe it&apos;s exceptionally chocolatey with a good body and hints of spice. There are many different flavor profiles that a coffee could have to score in this range. If you&apos;ve had a solid pour-over from a specialty cafe, it&apos;s probably at least this good. Assuming it has a flavor profile that&apos;s suitable for espresso, you could use it as the base for a small milk drink where the coffee&apos;s flavor shows through to bring something extra to the cup that elevates it above a good quality non-specialty coffee.</p><p>An 88 point coffee is exceptional. Expect something that&apos;s both coherent (notes are clear and play well with each other) and complex. It makes you think &quot;how can coffee taste like this?&quot; It smells good, it tastes good, and it takes you on a journey that you remember when you&apos;re done. It likely exhibits rarer notes like florals and complex fruits in a way that blends sweetness with a pleasing acidity. Often, the notes are more specific and strongly evoke a particular, refined flavor (e.g. instead of tasting like &quot;black tea&quot; it has notes of &quot;milk oolong&quot;, or instead of &quot;citrus-like&quot;, it&apos;s &quot;mandarin orange&quot; or &quot;yuzu&quot;, and instead of &quot;floral&quot; it&apos;s &quot;jasmine and honeysuckle&quot;). You&apos;d probably want to drink this coffee brewed fresh without adding anything to it (unless you really know what you&apos;re doing).</p><p>A 90+ point coffee will almost certainly be one of the best coffees you&apos;ve ever tasted in your life.</p><p>It&apos;s been really eye opening to encounter so many coffees of varying origins and qualities. I feel like I have a better reference now for what great (and terrible) coffee tastes like, and it helps me contextualize what I&apos;m experiencing when I try a new coffee for the first time.</p>]]></content:encoded></item></channel></rss>