<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss/styles.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>breq.dev</title><description>hey, i&apos;m brooke. this feed tracks blog posts and project writeups.</description><link>https://breq.dev/</link><item><title>Unlocking 60 minute rides on BlueBikes</title><link>https://breq.dev/2026/08/23/joy-rider/</link><guid isPermaLink="true">https://breq.dev/2026/08/23/joy-rider/</guid><description>How to earn Bike Angels points for fun and profit!</description><pubDate>Sun, 23 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Longtime readers of this blog will know that I am a big fan of biking in Boston, and especially, the &lt;a href=&quot;https://bluebikes.com/&quot;&gt;BlueBikes&lt;/a&gt; system. BlueBikes is a network of stations spread across the city with docks and bikes. You just go to any station, grab a bike, ride it to any other station, and dock it!&lt;/p&gt;
&lt;p&gt;The basic pricing for BlueBikes can be a bit high — $3 per ride is a bit more than subway fare for a transportation method you literally have to power yourself, and the base fare only covers 30 minutes of riding time. Worse, e-bikes cost an extra 33 cents per minute, which leads to some routes potentially being more expensive than the same trip on Uber depending on the time of day.&lt;/p&gt;
&lt;p&gt;The secret to making the system economical is memberships. While you can get a monthly membership, if you bike more than four months out of the year it makes more sense to get a yearly one. Members get an unlimited number of free 45-minute rides on non-electric bikes, and they pay a reduced e-bike price of 10 cents per minute with no unlock fee. I pay $133.50/year for a membership, and if I hadn’t had one, I would’ve paid $520 over the course of the year.&lt;/p&gt;
&lt;h2 id=&quot;bike-angels&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#bike-angels&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Bike Angels&lt;/h2&gt;
&lt;p&gt;But there’s one additional perk of memberships that you have to explore a bit to discover: Bike Angels! The Bike Angels program places a point value on each station: a station can have between 1 and 4 pickup points, or between 1 and 4 dropoff points, or no points. You earn points by picking up bikes from stations with pickup points, and by dropping off bikes at stations with dropoff points.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;A map showing BlueBikes stations near Kendall Square, most with dropoff point values attached.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;922&quot; height=&quot;921&quot; src=&quot;https://breq.dev/_astro/points-map.B3MhTjg-_ZVlJgW.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;Point values are visible on the station map. I have a 2x streak, causing the displayed points values to be doubled.&lt;/p&gt;
&lt;p&gt;Once you opt into the program, point values appear on your station map. For a while, I had the program turned on. I have a few stations near my work and a few near my home, so if one of those stations had points available, I could choose to take it to earn points on my commute. Over two years, I slowly accumulated 100 points.&lt;/p&gt;
&lt;p&gt;Bike Angels points can be redeemed for a few rewards, such as e-bike credits, an extension to your BlueBikes membership, or even gift cards. However, buried away in another section of the app is another reward: &lt;em&gt;Joy Rider&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Unlike the other rewards which draw points from your points bank, earning Joy Rider is based on the total lifetime accumulated points on your account. Once you reach 250 points, the amount of time you have to keep a bike unlocked goes from 45 minutes to 60 minutes for the lifetime of your account! I set my sights on this pretty much immediately after learning about it. In addition to bragging rights and feeling special, I’ve been wanting to get into more longer distance riding for pleasure and exercise. Being able to ride for 60 minutes at a time gives me more possibilities for destinations — I’ve been meaning to bike up to Salem, for instance, but there’s a large gap in stations along the route — and just makes riding around town more convenient when I don’t have to keep swapping bikes.&lt;/p&gt;
&lt;h2 id=&quot;game-theory&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#game-theory&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Game Theory!&lt;/h2&gt;
&lt;p&gt;One of the key aspects of earning Bike Angels points is managing your streak. If you take enough rides with a positive point value, you can multiply the points you earn by 3x, which massively increases the amount of points you can earn. This massively stacks the cards in favor of those who dedicate time for long Bike Angels runs: earning 100 points by taking a few one-off rides for my commute took me two years, but earning 100 points by doing rides purely for Bike Angels took me only two hours.&lt;/p&gt;
&lt;p&gt;Importantly, it doesn’t matter how many points you earn in each of the four positive rides you need to get to the maximum streak, so the best strategy is often to find some easy, low-points routes at the start so that your streak is fully charged up for more time consuming higher-points routes later on.&lt;/p&gt;
&lt;p&gt;Taking any ride which starts at a station with dropoff points or ends at a station with pickup points will end your streak. Since entire swaths of the city map are often covered in one or the other, it’s possible to get “stuck” surrounded by stations with dropoff points, requiring either a long walk or some clever transit use to get to a station where you can pick up a bike. The point values change every 15 minutes (on the :00, :15, :30, and :45), so keep an eye on them as they happen! It’s possible that the station you planned to drop your bike at becomes a station with pickup points, or vice versa, and you’ll need to change your plans.&lt;/p&gt;
&lt;p&gt;It’s worth occasionally zooming out on the station map to check for areas with high point values. In my experience, Kendall Square and Harvard Square are often great options. Since you’re already on a bike, getting between these regions is often quite easy!&lt;/p&gt;
&lt;p&gt;Generally, the best way to rack up points quickly is to find a station pair, and do loops of taking a bike from one, riding it to the other, and walking back. That might sound repetitive, but since points rebalance every 15 minutes, earning opportunities will constantly be moving around on the map and you’ll have to keep up. Ideally, you want to find two stations very close together for this — the distance you’re willing to walk should be proportional to the amount of points you’ll earn in the process (pickup points at the origin station plus dropoff points at the destination station). Sometimes that means taking a few beautiful 5-minute rides through Harvard Square for 12 points per ride, and sometimes that means moving a ton of bikes between stations that are only 200 feet away from each other and getting weird looks from onlookers. It’s a surprisingly varied experience!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;A screenshot of my ride history, showing rides ranging from +3 points to +12 points.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;922&quot; height=&quot;1658&quot; src=&quot;https://breq.dev/_astro/ride-history.BujPi7I5_1ovQvA.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;My rides ended up being a mix of longer runs for higher point values and shorter runs for lower point values.&lt;/p&gt;
&lt;p&gt;You can also chain various routes into a “milk run” style, which is especially useful for when you want to be in another area of town. When planning routes that aren’t loops, always look at the stations around your dropoff station and have an idea in mind for where you’ll pick up your next bike. If there’s an area with similar point values at all of the stations, picking the one closest to a station where you can pick up another bike can save you a lot of time!&lt;/p&gt;
&lt;p&gt;It might seem like you can totally game the system if you find two stations close together by moving tons of bikes between them during one 15-minute interval, causing the point values to flip to the other direction, then moving them all back during the next 15-minute interval, and so on… This does work, but they will ban you if they catch you. Also, it’s a lot less fun in my opinion.&lt;/p&gt;
&lt;h2 id=&quot;why-do-bike-angels&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#why-do-bike-angels&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Why do Bike Angels?&lt;/h2&gt;
&lt;p&gt;So, why should you do grunt work for the BlueBikes system operators? While it’s the only way to get Joy Rider, you’d probably have to value your time and energy pretty low for it to be worth it on its own for the membership extensions and gift cards. But beyond the perks, I really enjoyed the experience overall and will definitely be doing some Bike Angels farming again!&lt;/p&gt;
&lt;p&gt;Doing Bike Angels requires a lot of quick thinking and problem solving. Maybe your plan for getting your next bike fell through when a station switched from pickup to dropoff points, or maybe you’re trying to decide whether to head towards downtown Cambridge or downtown Boston, or maybe you’re trying to decide which of two station pairs give you the best options for earning points. Knowledge of the city layout, the locations of stations and bike lanes, and even the transit routes nearby can be really helpful. (The Cambridge EZRide saved me on one occasion when I was stuck in the heart of Kendall and the nearest pickup points station was a 30 minute walk away!) The best way I can describe it that it’s kind of like playing a scavenger hunt or an augmented reality game like Pokemon Go.&lt;/p&gt;
&lt;p&gt;Seeing different parts of the city was really fun, too. I got to see parts of the city I had never been on before, from quiet residential neighborhoods to blocks full of office buildings. After getting lost in the Novartis campus, discovering countless city parks I had no idea existed, and crossing lots of new routes and trails off my bucket list, I can confidently say that this experience contributed so much to my understanding of and my appreciation for the place I live.&lt;/p&gt;</content:encoded></item><item><title>Boston&apos;s earliest buses</title><link>https://breq.dev/2026/07/08/early-buses/</link><guid isPermaLink="true">https://breq.dev/2026/07/08/early-buses/</guid><description>The little-known system of early-morning MBTA routes</description><pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Here in Boston, our subway trains stop running around 1 AM and don’t start until after 5 AM the next day. So what do you do if you need to get somewhere early, like catching an early-morning flight or getting downtown in time to watch the sunrise?&lt;/p&gt;
&lt;p&gt;Lucky for you, the MBTA operates a network of special early-morning bus routes that converge downtown at Haymarket Station and at Logan Airport. If you weren’t aware of this, it might be because the only official indication that they exist at all can be found from tiny footnotes in the schedule pamphlets or the “uncommon destinations” dropdown on each route’s webpage.&lt;/p&gt;
&lt;p&gt;That said, myself and my friends can vouch for the fact that these mysterious buses are real! We’ve used them to get downtown in time to watch the sunrise, to catch an early morning flight, as part of a 24-hour scavenger hunt, and for even more adventures.&lt;/p&gt;
&lt;p&gt;In an attempt to demystify these routes, I spent some time creating a “subway-style” map of the network, showing intermediate stops and departure times.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1587&quot; height=&quot;1123&quot; src=&quot;https://breq.dev/_astro/early-bus-network.b1acsZ10_Z164Bt7.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-2 max-w-sm mx-auto gap-2 my-4&quot;&gt;
&lt;a class=&quot;bg-blue-200 dark:bg-blue-800 hover:bg-blue-300 dark:hover:bg-blue-700 transition-colors text-center rounded-xl text-black! dark:text-white! no-underline! *:mb-0! py-2&quot; href=&quot;https://breq.dev/images/early-buses/early-bus-network.png&quot; download=&quot;MBTA Early Morning Bus Network.png&quot;&gt;
&lt;p&gt;Download PNG&lt;/p&gt;
&lt;/a&gt;
&lt;a class=&quot;bg-blue-200 dark:bg-blue-800 hover:bg-blue-300 dark:hover:bg-blue-700 transition-colors text-center rounded-xl text-black! dark:text-white! no-underline! *:mb-0! py-2&quot; href=&quot;https://breq.dev/images/early-buses/early-bus-network.svg&quot; download=&quot;MBTA Early Morning Bus Network.svg&quot;&gt;
&lt;p&gt;Download SVG&lt;/p&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;To make this map, I included every inbound route that departs before 5 AM, arrives before 5:20 AM, and is useful to get to either downtown or the airport (i.e., excluding routes designed to connect with the first inbound subway trains, or bus routes with an unusually early outbound trip due to the lack of a garage near their outbound terminus).&lt;/p&gt;
&lt;p&gt;One thing I tried to emphasize is that there’s a surprising amount of opportunity for chaining these routes together. From Mattapan, taking either Route 28 departure lines you up nicely to transfer at Nubian to the corresponding 15/171 departure to the airport. The 89/93 and the 57 both arrive at Haymarket early enough that you can transfer to the 116 to reach the airport. Or, if you’re going somewhere other than terminal C and want to skip the walk, you can transfer from the 57 to the first Logan Express Back Bay bus of the day.&lt;/p&gt;
&lt;p&gt;The best existing resource I could find before drawing this map is this &lt;a href=&quot;https://www.bostonmpo.org/data/calendar/htmls/2013/MPO_0711_Early_Morning_Transit.html&quot;&gt;report from the Boston Metropolitan Planning Organization&lt;/a&gt;. Although it’s an excellent academic resource, it’s not directly useful as a bus user as it uses the non-public internal identifiers for each bus route instead of the public ones and it lacks the actual timetable information. My research for making this map came from a mix of that report and some Python scripts I used to extract the earliest routes from the MBTA’s GTFS data.&lt;/p&gt;
&lt;p&gt;If you’re still dubious that these mysterious bus routes even exist, here are some photos from my lovely friend &lt;a href=&quot;https://june.quest/&quot;&gt;June&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3072&quot; height=&quot;4080&quot; src=&quot;https://breq.dev/_astro/89-93-haymarket.DUlqWF0H_1XG77R.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3072&quot; height=&quot;4080&quot; src=&quot;https://breq.dev/_astro/116-terminal-c.Qg9TFVpu_1SKplF.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;</content:encoded></item><item><title>Meshtastic on an Adafruit MagTag</title><link>https://breq.dev/projects/meshtag/</link><guid isPermaLink="true">https://breq.dev/projects/meshtag/</guid><description>Repurposing an E-Ink board by hiding a LoRa radio module inside</description><pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/front_left_close.CohWc62n_2uL2LX.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;This hack rewires an &lt;a href=&quot;https://www.adafruit.com/product/4800&quot;&gt;Adafruit MagTag&lt;/a&gt; to function as a handheld &lt;a href=&quot;https://meshtastic.org/&quot;&gt;Meshtastic&lt;/a&gt; node. The device runs &lt;a href=&quot;https://meshtastic.org/docs/configuration/device-uis/baseui/&quot;&gt;BaseUI&lt;/a&gt; on the E-Ink panel and is controlled via the four front buttons. The Meshtastic firmware port supports ambient lighting, battery voltage monitoring, and I2C sensors. Adapting a stock MagTag can be done relatively easily with a replacement 3D-printed rear cover, a Seeed Studio &lt;a href=&quot;https://www.seeedstudio.com/Wio-SX1262-Wireless-Module-p-5981.html&quot;&gt;Wio-SX1262&lt;/a&gt; module, and some bodge wires.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;A few months ago, my girlfriend &lt;a href=&quot;https://miakizz.quest/&quot;&gt;Mia&lt;/a&gt; bought a &lt;a href=&quot;https://www.adafruit.com/product/4800&quot;&gt;MagTag&lt;/a&gt; from Adafruit. It’s a small board with a microcontroller, battery, WiFi, and an E-Ink display. I think the intended use is that you attach magnets to the back and stick it onto your fridge so you can have an always-on display showing relevant information (weather, transit, etc) at a glance, kind of like a tiny DIY version of a &lt;a href=&quot;https://trmnl.com/&quot;&gt;TRMNL&lt;/a&gt; but with some extra buttons and sensors.&lt;/p&gt;
&lt;p&gt;Her idea was originally to use it as a pager, relying on a Bluetooth connection to the user’s phone. We got unlucky, though: the MagTag uses an ESP32S2, which is the &lt;a href=&quot;https://dl.espressif.com/public/Espressif-SoC-Portfolio-2026.pdf&quot;&gt;only popular ESP32 model that does not include Bluetooth&lt;/a&gt;. As such, it sat on a shelf for a while.&lt;/p&gt;
&lt;p&gt;Around this time, I started getting interested in the &lt;a href=&quot;https://meshtastic.org/&quot;&gt;Meshtastic&lt;/a&gt; project. Meshtastic is a protocol that enables mesh networking using &lt;a href=&quot;https://en.wikipedia.org/wiki/LoRa&quot;&gt;LoRa&lt;/a&gt; radio chips. The unique &lt;a href=&quot;https://en.wikipedia.org/wiki/Chirp_spread_spectrum&quot;&gt;“chirp spread spectrum”&lt;/a&gt; modulation employed by LoRa allows for long range communication even at low power, allowing the radios to be used without an amateur radio license and, therefore, with encryption. Meshtastic takes full advantage of this by giving each node a public/private keypair and supporting end-to-end encryption of messages.&lt;/p&gt;
&lt;p&gt;I live in a dense, nerdy city, so I figured it was pretty likely that there would be a substantial number of nodes forming a dense mesh nearby. As it turns out, I was right! I realized pretty quickly that I wanted to get something I could take places more easily, as opposed to the &lt;a href=&quot;https://www.seeedstudio.com/XIAO-ESP32S3-for-Meshtastic-LoRa-with-3D-Printed-Enclosure-p-6314.html&quot;&gt;barebones USB-powered node&lt;/a&gt; I started with.&lt;/p&gt;
&lt;p&gt;As I was scanning through the various &lt;a href=&quot;https://meshtastic.org/docs/hardware/devices/&quot;&gt;Meshtastic devices&lt;/a&gt; available, I realized that many of them looked almost identical to the MagTag: an E-Ink display, a few buttons, a battery, and an ESP32 chip. It seemed like the only thing I would need to add would be a LoRa radio, and I could have a capable handheld node!&lt;/p&gt;
&lt;h1 id=&quot;mechanical-design&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#mechanical-design&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Mechanical Design&lt;/h1&gt;
&lt;p&gt;The MagTag is designed to be used with a particular &lt;a href=&quot;https://www.adafruit.com/product/6433&quot;&gt;enclosure&lt;/a&gt; — it’s easy to FDM print it, and Adafruit sells SLA printed ones that look nicer. As is, the enclosure is almost perfect for what we need! Because the thickness of the hosing is dictated by the standoffs on the board, there is a lot of empty room behind the PCB for us to put the radio module. The only thing missing is a spot to put the antenna!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3072&quot; height=&quot;4096&quot; src=&quot;https://breq.dev/_astro/back_right.D2lbHtFn_ZOQIf8.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;The 3D-printed MagTag case has a convenient built-in stand.&lt;/p&gt;
&lt;p&gt;I went with a &lt;a href=&quot;https://www.seeedstudio.com/External-Antenna-868-915MHZ-2dBi-SMA-L195mm-Foldable-p-5863.html&quot;&gt;2 dBi foldable antenna&lt;/a&gt; from Seeed Studio, which meant I only needed to include an SMA port on the outside of the enclosure.&lt;/p&gt;
&lt;p&gt;I’ve been learning &lt;a href=&quot;https://www.onshape.com/en/&quot;&gt;Onshape&lt;/a&gt; recently, and this was a great project for it — modifying existing designs is something SketchUp really struggles at. With Onshape, I was able to load in the STEP file, make a sketch off of the wall where I wanted the antenna hole, and extrude the hole in the position I wanted it. I also added more material on the outside/bottom to better support the washer, and cut out part of the inside case to ensure clearance for the end of the SMA connector.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1481&quot; height=&quot;898&quot; src=&quot;https://breq.dev/_astro/cad_sketch.9GbC0ifF_Z5Ub0u.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto my-4&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1386&quot; height=&quot;766&quot; src=&quot;https://breq.dev/_astro/cad_outside.Bf7VZaKp_15ErjS.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1386&quot; height=&quot;766&quot; src=&quot;https://breq.dev/_astro/cad_inside.Dqe1YKyg_ISHr5.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;caption&quot;&gt;
&lt;p&gt;The link to view the design is &lt;a href=&quot;https://cad.onshape.com/documents/5d17302d9e1954251f45f057/w/bf64ea3695e203d76b93e4d8/e/f66f4a96e99ae47a7234f0a1?renderMode=0&amp;#x26;uiState=6a432ad63a49f9c46f6ba8e8&quot;&gt;here&lt;/a&gt;!&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;As it turned out, I had plenty of room to fit the SMA connector while routing the internal coax cable nicely above the stand cutout!&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto my-4&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/antenna_port.bGYq8Ww0_wRgTb.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/antenna_wiring.Bsv7TJzx_gpTSl.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h1 id=&quot;wiring&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#wiring&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Wiring&lt;/h1&gt;
&lt;p&gt;For a LoRa module, I chose the &lt;a href=&quot;https://www.seeedstudio.com/Wio-SX1262-Wireless-Module-p-5981.html&quot;&gt;Wio-SX1262&lt;/a&gt; from Seeed Studio. The SX1262 seems like the most popular choice nowadays for a LoRa chip, and the Wio module has all of the bells and whistles like a TX/RX switch for the antenna and a temperature-compensated crystal oscillator.&lt;/p&gt;
&lt;p&gt;In terms of actually connecting it to the ESP32, my first order of business was figuring out what GPIO pins I could steal.&lt;/p&gt;
&lt;p&gt;The SX1262 uses SPI for most communication, which is great since it can be chained on the same SPI pins used for the E-Ink panel and the ESP32 module’s internal PSRAM. That said, there are a few extra signals I needed to find spare pins for.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GPIO2:&lt;/strong&gt; This is the only GPIO unused on the MagTag. However, it &lt;a href=&quot;https://esp32.com/viewtopic.php?t=41178&quot;&gt;has to be held low&lt;/a&gt; during boot, and has a pull-down resistor on it. This precludes its use as an input, but it can still be used as an output! I mapped this to &lt;strong&gt;NSS&lt;/strong&gt; (SPI chip select) on the SX1262.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GPIO18:&lt;/strong&gt; This is one of the two DAC pins on the ESP32, and the MagTag breaks it out to a 3-pin JST connector at the bottom labeled “A1” for users to connect their own hardware. Since this pin can be used as an input, I wired it to &lt;strong&gt;DIO1&lt;/strong&gt; (an interrupt signal) from the SX1262.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GPIO10:&lt;/strong&gt; This is just a plain digital pin, and the MagTag breaks it out to a 3-pin JST connector labeled “D10.” Again, assuming nothing is connected to that port, we can use this as an input, so I wired it to &lt;strong&gt;BUSY&lt;/strong&gt; on the SX1262 (which indicates when the module cannot receive SPI commands).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GPIO13:&lt;/strong&gt; This pin is connected to an LED on the back of the MagTag intended for debugging (it’s not visible on the front). I wired it to &lt;strong&gt;NRST&lt;/strong&gt; (reset) on the SX1262.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This repurposing gives us enough pins to wire up the radio module, while keeping almost all of the MagTag functionality intact and avoiding the need to make any destructive changes to the PCB.&lt;/p&gt;
&lt;p&gt;Note that the SX1262 actually has three “DIO” pins! The Wio module connects DIO2 to the RF switch internally (meaning we don’t need to wire to its RF_SW pin), and DIO3 is used for the temperature compensated oscillator.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/wio_wiring.c2gJkZYm_ZPaMq3.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;When I bought the module, I saw that it had castellated edges that I figured would be convenient to solder to. I did not realize they were only a 1 mm pitch! After unsuccessfully attempting to wire to them with the 22 AWG hookup wire I had lying around, I bought some 30 AWG wire that made the process much easier.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3072&quot; height=&quot;4096&quot; src=&quot;https://breq.dev/_astro/esp32_wiring.CHivoynD_21is9v.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;To test my pin assignments and make sure I didn’t miss anything about the ESP32’s GPIO capabilities, I connected the Wio to the GPIOs with lots of slack at first so I could easily reassign pins if needed. Everything seemed to work on the first try!&lt;/p&gt;
&lt;p&gt;For those following along at home, here’s the final wiring table:&lt;/p&gt;























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Wio pin&lt;/th&gt;&lt;th&gt;ESP32 pin name&lt;/th&gt;&lt;th&gt;ESP32 pin&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;VCC&lt;/td&gt;&lt;td&gt;3V3&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GND&lt;/td&gt;&lt;td&gt;GND&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;NSS&lt;/td&gt;&lt;td&gt;IO2&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;NRST&lt;/td&gt;&lt;td&gt;IO13&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SCK&lt;/td&gt;&lt;td&gt;IO36&lt;/td&gt;&lt;td&gt;30&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;MOSI&lt;/td&gt;&lt;td&gt;IO35&lt;/td&gt;&lt;td&gt;29&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;MISO&lt;/td&gt;&lt;td&gt;IO37&lt;/td&gt;&lt;td&gt;31&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;BUSY&lt;/td&gt;&lt;td&gt;IO10&lt;/td&gt;&lt;td&gt;13&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;DIO1&lt;/td&gt;&lt;td&gt;IO18&lt;/td&gt;&lt;td&gt;21&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;To permanently mount the Wio module, I decided to attach it to the top of the ESP32’s RF shield. While I would’ve preferred a more solid mount, neither the MagTag nor the Wio really had any rigid features to mount to.&lt;/p&gt;
&lt;p&gt;After shortening and re-soldering the wires, I put some electrical tape on the RF shield so that it wouldn’t cause any shorts, then used some E6000 adhesive to attach the Wio module. It’s certainly not the most elegant approach, but it seemed like the best I could do with the tools that I had. I clamped everything in a Panavise for a few hours to let everything set.&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto my-4&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3072&quot; height=&quot;4096&quot; src=&quot;https://breq.dev/_astro/final_wiring_close.Db6xlUSJ_2cHtf6.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3072&quot; height=&quot;4096&quot; src=&quot;https://breq.dev/_astro/glue_clamp.DpVyIEMn_1BvbV7.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;With the Wio module all the way to one side on the RF shield (centering it would cause it to collide with the stand), everything fit nicely and I was able to close up the enclosure!&lt;/p&gt;
&lt;h1 id=&quot;firmware&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#firmware&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Firmware&lt;/h1&gt;
&lt;p&gt;Adding support for a new board to Meshtastic means creating a “variant.h” file which defines macros for pin mappings, hardware types, and configuration parameters.&lt;/p&gt;
&lt;p&gt;As it turns out, most of the features on the MagTag were already supported by the Meshtastic firmware, so it was just a matter of adding the right &lt;code&gt;#define&lt;/code&gt; statements for the specific pin mappings of the MagTag board.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/full_disassembly.BDFSJwR7_Z15W1K3.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The obvious place to start was the LoRa radio. Configuring the SX1262 was straightforward using the SPI pin mappings.&lt;/p&gt;
&lt;p&gt;The E-Ink display put up slightly more of a fight, but not much! Meshtastic uses the &lt;a href=&quot;https://github.com/ZinggJM/GxEPD2&quot;&gt;GxEPD2&lt;/a&gt; library for controlling E-Ink displays. While it’s intended for use with Waveshare brand display boards, I found through some googling that the SSD1680 chip display used in the MagTag corresponds to the “GxEPD2_290_T94_V2” preset.&lt;/p&gt;
&lt;p&gt;The Meshtastic UI looks great on this panel! The firmware makes extensive use partial refresh, a technique where the display contents are updated but the full E-Ink refresh cycle is skipped. This approach typically results in distracting ghosting, but it’s not all that noticeable here. The only hiccup I’m experiencing is that display updates are sometimes slow enough to make the UI miss repeated button presses.&lt;/p&gt;
&lt;p&gt;Most Meshtastic boards are designed around a single-button interface (press for “next”, hold for “select”). The MagTag has four buttons at the front, and its silkscreen indicates that they are intended to be used as directional controls (left/up/down/right). The closest thing that Meshtastic supports comes from the &lt;a href=&quot;https://meshtastic.org/docs/hardware/devices/lilygo/tdeck/?t-deck=t-deck&quot;&gt;LILYGO T-Deck&lt;/a&gt;, which uses a trackball to provide four-directional input. If we configure our four individual buttons using the trackball driver, we get a pretty smooth way to navigate the UI! And while the MagTag doesn’t have a “select” button on the front, we can map the BOOT button on the side. (Annoyingly, this button is directly above the RESET button and they look identical — so don’t press the wrong one when you’re deep in a menu…)&lt;/p&gt;
&lt;p&gt;Like most Adafruit boards with a LiPo battery, the MagTag includes a voltage divider connected to an ADC pin so that user code can read the current battery voltage. Meshtastic directly supports this type of battery reading with a few config options, and can use the battery voltage to produce both a “percent remaining” indicator and to detect if the battery is charging or not.&lt;/p&gt;
&lt;p&gt;One of the quirks of Meshtastic is its &lt;a href=&quot;https://meshtastic.org/docs/configuration/module/telemetry/&quot;&gt;I2C sensor detection&lt;/a&gt; setup: it can scan the node’s I2C bus and look for sensors, then broadcast that data to the mesh. This functionality can be used to run a public weather reporting station, or just to make sure a node deployed in a remote location doesn’t overheat. This pairs nicely with the &lt;a href=&quot;https://learn.adafruit.com/introducing-adafruit-stemma-qt/what-is-stemma&quot;&gt;STEMMA QT&lt;/a&gt; connector exposed on the bottom of the MagTag, which supports connecting and chaining various I2C-based sensors.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3072&quot; height=&quot;4096&quot; src=&quot;https://breq.dev/_astro/front.Dxc0yk-m_Z12uQpN.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The MagTag also includes four sideways-firing NeoPixels which are supposed to illuminate the E-Ink panel. Meshtastic does support NeoPixels through its &lt;a href=&quot;https://meshtastic.org/docs/configuration/module/ambient-lighting/&quot;&gt;“ambient lighting” module&lt;/a&gt;. Unfortunately, it seems like alignment of the NeoPixels isn’t great for this use — I found it didn’t offer an improvement in readability in any lighting conditions I tried.&lt;/p&gt;
&lt;h1 id=&quot;conclusion&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/front_top.9zhu9nRX_1zTEdd.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Overall, I’m really pleased with how well this idea came together! I can use my little MagTag node for sending and viewing public messages on the mesh, directly messaging other Meshtastic users in my area, and viewing the list and map of nearby nodes and their signal strength.&lt;/p&gt;
&lt;p&gt;While there are definitely some shortcomings like the lack of Bluetooth, this board ended up being a pretty capable Meshtastic node. I’m sure if I spent more time adapting the firmware, I could get a smoother user experience, but it’s honestly quite good as it is. I’m excited to continue using this node and I think it’s an excellent companion to my fixed node at home.&lt;/p&gt;</content:encoded></item><item><title>Finding my perfect laptop</title><link>https://breq.dev/2026/06/16/laptops/</link><guid isPermaLink="true">https://breq.dev/2026/06/16/laptops/</guid><description>Comparing my experience with a MacBook, ThinkPad, and Framework Laptop</description><pubDate>Tue, 16 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/stickers.CxIb19Tk_ZVQ10a.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Recently, I came to the realization that within the past year, I’ve tried daily-driving a ThinkPad running Windows, a Framework Laptop running Linux, and a MacBook Pro running macOS. They’re all great laptops running solid operating systems, but they all mesh slightly differently to the way I like to work. (Spoiler alert: I really like my Framework.)&lt;/p&gt;
&lt;h1 id=&quot;what-makes-a-good-laptop-for-me&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#what-makes-a-good-laptop-for-me&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;What makes a good laptop for me?&lt;/h1&gt;
&lt;p&gt;Portability is probably the most important factor for me. I work best when I’m moving around! As a student, I tended to move from one building to another every hour or two to break up the monotony of studying. At every job I’ve had since, I’ve been constantly moving between my desk and the rooms where the engineering happens. And at home, I’ll get work done at my desk, at the dining room table, on the couch, in bed, at a nearby park… you get the picture!&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4000&quot; height=&quot;3000&quot; src=&quot;https://breq.dev/_astro/rooftop.CTguXnKm_Z1j363v.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/woods.DCMZW5pZ_1bQaYH.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/park.D38IX90D_1rdcCp.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4000&quot; height=&quot;3000&quot; src=&quot;https://breq.dev/_astro/drink.Cqzc_bYY_ZiKVOk.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;I find a 13” screen to be around the ideal size. I know a lot of people who work in tech who think using a screen that small is outrageous, but it works well for me! Extra screen space is nice, but not when it comes at the expense of the laptop’s ability to fit into a tote bag, its weight while I’m trying to use it on my lap, or my ability to use it on a bus or plane without bumping elbows with the other people in my row. The only time I tried a 16” laptop was during my internship at Amazon Robotics, and I pretty quickly realized it wasn’t for me.&lt;/p&gt;
&lt;p&gt;I’ve gotten better over time at maximizing the utility I get out of a smaller screen by using keyboard shortcuts and trackpad gestures to switch between menus and apps, and I have good enough vision to run most apps at a small UI scaling setting. I know the next step for me to use a small screen as efficiently as possible would be for me to learn how to use a tiling window manager, but I don’t think I’m ready to make that jump yet. I also have a larger monitor at my desk which I can move to if I need additional screen space for something specific.&lt;/p&gt;
&lt;p&gt;On the topic of screens: I find that touchscreens add no value for me for the type of work I do on a computer. If you make digital art and use a stylus, or you like the feeling of handwriting notes instead of typing them, or you just like scrolling websites with your fingers on the screen, you’ll probably feel differently! But personally, I’m content with a trackpad.&lt;/p&gt;
&lt;p&gt;Software stability is another big factor for me. I’ve learned that I am not the type of person who likes to tweak my computer to be as efficient for me as possible — I’m fine making a bunch of customizations, but the moment they stop working, I lack the patience to fix anything! The laptop and its operating system should give me reasonable defaults, work consistently and reliably, follow the &lt;a href=&quot;https://en.wikipedia.org/wiki/Principle_of_least_astonishment&quot;&gt;principle of least astonishment&lt;/a&gt;, and get out of my way so I can get work done.&lt;/p&gt;
&lt;h1 id=&quot;macbook-pro&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#macbook-pro&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;MacBook Pro&lt;/h1&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3751&quot; height=&quot;2813&quot; src=&quot;https://breq.dev/_astro/bonjour.CSU9ll3o_Z1s9dhA.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/train.BKwMAjK4_1GgY2B.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;When my old laptop broke literally the day after I moved into my college dorm for freshman year, I needed to find something as fast as possible, so I took the train to the Apple store and bought a base model 13” M1 MacBook Pro.&lt;/p&gt;
&lt;p&gt;Considering the rushed nature of the purchase, I think I ended up with a pretty great computer! During my freshman year, having an Arm CPU meant that I was the odd one out when it came to installing software for classes, but that became less of an issue over the next few years. My only regret is getting only 8 GB of RAM, leaving the computer woefully underpowered for running virtual machines or even just the IntelliJ IDEs I had to use for coursework. I likely would’ve kept it for another year or two had it been the higher spec model.&lt;/p&gt;
&lt;p&gt;I do genuinely miss the Touch Bar. It made things like changing volume settings much more ergonomic than mashing a key repeatedly, and I got to &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=felipe.nasc-touchbar&quot;&gt;map my favorite VS Code hotkeys to nice icons&lt;/a&gt; instead of having to remember which action “F2” maps to.&lt;/p&gt;
&lt;p&gt;At the time, the M1 laptops were excellent value for money. I think Apple’s current lineup is a bit worse off in that regard, but I suppose that was just the discount I got for being an early adopter of Apple Silicon. Speaking of that, the battery life on that computer was amazing — a full day of classes followed by staying up late in the robotics lab. Battery life is less of a concern for me now, but at the time it was incredibly helpful.&lt;/p&gt;
&lt;p&gt;The laptop had only two USB-C ports. I honestly didn’t mind that too much, I could carry a dongle for everything else. What was more annoying was only having ports on one side of the laptop, in situations where a charging cable could just barely reach.&lt;/p&gt;
&lt;p&gt;During its four-year run as my daily driver, that laptop took multiple falls onto the concrete floor of the robotics lab without much damage. It definitely accumulated some dings and scratches, but nothing that interfered with its function. The only feature that stopped working over the years was part of the backlit keyboard after I spilled hot chocolate on it once.&lt;/p&gt;
&lt;h2 id=&quot;macos&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#macos&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;macOS&lt;/h2&gt;
&lt;p&gt;The software was, frankly, pretty jank! In the first year or so, memory leaks in macOS were a constant nightmare requiring frequent reboots, but that was greatly improved by later updates. The computer would also occasionally flash the display bright magenta and hard reboot about 30 seconds after logging in.&lt;/p&gt;
&lt;p&gt;That said, macOS in general is going to be pretty great for software development. I appreciated the similarity to the Linux environments I was most familiar with. The only times I needed to switch to doing my work in a Linux VM was when using ROS, which builds packages for Ubuntu only.&lt;/p&gt;
&lt;p&gt;I definitely missed having a “real” package manager. Homebrew is fine, and works about as well as a third-party package manager can, but it’s just plain awkward to not have a package manager built into the operating system.&lt;/p&gt;
&lt;p&gt;While I was a student, I needed or wanted to run apps like Microsoft Word and Adobe Photoshop using my student license, and macOS was the perfect option that gave me those without needing to resort to dual-booting. That said, since graduation, I’ve been daily-driving Linux just fine without a Windows partition.&lt;/p&gt;
&lt;h1 id=&quot;thinkpad-x1-carbon&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#thinkpad-x1-carbon&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;ThinkPad X1 Carbon&lt;/h1&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2934&quot; height=&quot;2934&quot; src=&quot;https://breq.dev/_astro/cat.CD_hBCh4_1WEE1s.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1251&quot; height=&quot;1251&quot; src=&quot;https://breq.dev/_astro/table.B-93k_Q-_Z2mdaWV.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;I use a ThinkPad X1 Carbon Gen 12 at work. It’s a similar size with a 14” display.&lt;/p&gt;
&lt;p&gt;It’s a well-built laptop for sure. I can’t say I’ve put it through as hard of a test as my MacBook but it feels pretty sturdy. It’s a lot lighter weight than other laptops I’ve used, but that’s honestly not something I care about much — I have a comfortable backpack.&lt;/p&gt;
&lt;p&gt;Windows Hello support is somewhat unreliable (it seems to show a “Sorry something went wrong” message about 20% of the time). I honestly would prefer just a standard fingerprint reader.&lt;/p&gt;
&lt;p&gt;I never got the hang of using a TrackPoint™-style pointer. I know some people swear by it, but I just can’t get the precision I need.&lt;/p&gt;
&lt;p&gt;Honestly, I don’t have a lot more to say about it. It’s a good laptop, but I don’t think I would ever buy one for personal use given how expensive it is. I’m sure the value of used ones is also quite a bit better, but buying laptops used is a whole world I have no experience with.&lt;/p&gt;
&lt;p&gt;I run Windows on this laptop (again, work), but I can see why people use these as Linux laptops. They’re simple, no-frills, solid machines for those with the budget for them.&lt;/p&gt;
&lt;h2 id=&quot;windows&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#windows&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Windows&lt;/h2&gt;
&lt;p&gt;Doing software development on Windows is definitely better than it was back in the day, but it still feels like I’m working atop a pile of leaky abstractions which all just try to emulate the way Linux does things. There are like three different ways to install Python and they all have tradeoffs that are totally non-obvious. My terminal has four options when I open a new tab somehow. The Rust installer makes you &lt;a href=&quot;https://internals.rust-lang.org/t/use-lld-by-default-on-windows-to-mitigate-microsoft-c-build-tools-licencing-issues/16606&quot;&gt;download parts of Visual Studio&lt;/a&gt; which has weird legal ramifications. There’s always either the “windows-y” way to do something or the “almost-linux-y” way of doing it.&lt;/p&gt;
&lt;p&gt;When I’m in the flow I can almost forget I’m working on a Windows machine, and that’s about all I can hope for. The set of tooling I use at work is a lot simpler than the vast collection of stuff I use at home for one thing or another, which helps. But I know that reinstalling everything in my development environment would be a much harder process than doing the same on a Linux machine.&lt;/p&gt;
&lt;p&gt;It is nice to be able to easily run whatever weird Windows-only software a vendor supplies with their product. I’ve had to resort to lots of nonsense like dual-booting in the past to flash firmware or change the configuration on motor controllers and the like.&lt;/p&gt;
&lt;h1 id=&quot;framework&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#framework&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Framework&lt;/h1&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/ari.R-Lz6QKo_IJYYC.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3399&quot; height=&quot;2550&quot; src=&quot;https://breq.dev/_astro/disassembly.Hqik8bsx_Z2b4Dpo.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3385&quot; height=&quot;2539&quot; src=&quot;https://breq.dev/_astro/bottom.DemjGbjn_Z1PSQu9.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/stickers.CxIb19Tk_ZVQ10a.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;As a (very belated) college graduation present to myself, I decided to buy myself the Ryzen 7040 series Framework 13 since several of my friends have the same model and it seemed like a good value for money.&lt;/p&gt;
&lt;p&gt;It is extremely fun to have a cute laptop, with a fun purple bezel and pink expansion cards. This is the first laptop I’ve ever put stickers on, and it feels great! For some reason, it feels less like I’m putting ugly patches over the cold industrial design of my computer and more like I’m adding even more fun and color to a laptop that already shows it.&lt;/p&gt;
&lt;p&gt;It feels less sturdy than my MacBook, but honestly not by that much. So far it has held up really well! And it’s comforting to know that if I broke something, I could easily just replace that part instead of the entire machine.&lt;/p&gt;
&lt;p&gt;I really like the 3:2 aspect ratio display. I didn’t even know the aspect ratio would be different until after I bought the laptop, but it’s quickly become one of my favorite features. It’s not a huge difference, but it definitely helps for things like coding or web browsing. My other two laptops had a 16:10 display, so this is just a little bit taller. The relatively narrower width is also helpful when working on public transit, for instance.&lt;/p&gt;
&lt;p&gt;The battery life is pretty good — it’s better than the ThinkPad but worse than the MacBook. That said, there are a few specific things like video calling that drain the battery and heat up the laptop much faster than I’m used to.&lt;/p&gt;
&lt;p&gt;I wasn’t sure how important the expansion cards would be for me. I do like having full-size display and USB ports again, but I don’t really swap them around often. I have an Ethernet expansion card, since I find having a hardware Ethernet port really nice for troubleshooting networking issues as opposed to having a dongle dangling around, but I haven’t actually had a reason to use it yet.&lt;/p&gt;
&lt;p&gt;The trackpad is pretty nice overall. Palm rejection is not as good as what I’m used to on the MacBook, but still rarely gets in the way.&lt;/p&gt;
&lt;h2 id=&quot;fedora-linux&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#fedora-linux&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Fedora Linux&lt;/h2&gt;
&lt;p&gt;Daily-driving Linux has been a lot easier than I thought it would be, considering the last time I did it was in high school. I’m using Fedora now instead of Ubuntu at the advice of my girlfriends. It’s nice and stable, the GNOME UI looks great, and trackpad gestures work just as well as they did on my MacBook.&lt;/p&gt;
&lt;p&gt;I think I might’ve fallen victim to the frequency illusion: Linux users constantly joke about problems with Wi-Fi, sound, suspend/resume, installing apps sometimes being complicated, and so on. Despite this, the experience with these has legitimately gotten much better over time — installing common applications is much easier than it was back when I last used Linux on my personal laptop in high school. It’s also a function of what system you’re installing things onto and how much that vendor cares about Linux support. As it turns out, Framework cares a lot!&lt;/p&gt;
&lt;p&gt;Closed-source apps used to be kind of hit-or-miss, but are at least relatively consistent now. Spotify has a weird border around the window and RealVNC has some weird scaling issues, but Discord finally fixed their Linux updater so that runs as smooth as it does on Mac and Windows.&lt;/p&gt;
&lt;p&gt;The fingerprint reader in some ways feels more integrated into the operating system than Touch ID did. For instance, it works instead of a password for &lt;code&gt;sudo&lt;/code&gt; in the terminal without any extra setup.&lt;/p&gt;
&lt;p&gt;I had an issue for the first few months with my laptop waking up from sleep unreliably — it turns out I had the &lt;a href=&quot;https://community.frame.work/t/framework-13-amd-7040-reboot-after-resume-because-of-hdmi/75409&quot;&gt;HDMI expansion card in the wrong port&lt;/a&gt;! I’m a little annoyed that the symptom of that would be “opening the lid to resume from sleep results in a hard reboot about 10% of the time,” but I’m at least glad it’s resolved.&lt;/p&gt;
&lt;h1 id=&quot;conclusion&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Overall, I’m really happy with my decision to switch from a MacBook to a Framework laptop! It’s nice to be back in a Linux environment for development work, the software is about as stable as it was on my Mac (if not better), and I really am not giving much up in terms of battery life or performance.&lt;/p&gt;
&lt;p&gt;I do like the upgradeability of the Framework laptop, but it’s honestly not the main reason I bought it. I just wanted a laptop that ran Linux, that was portable enough to fit the way I like to work, and with software that felt stable to use on a day-to-day basis. That said, I know it’ll come in handy a few years down the line when I’m ready for an upgrade — and I’m excited that I’ll get to keep all the stickers I’ve collected when that time comes!&lt;/p&gt;</content:encoded></item><item><title>Matter integration for LED matrices</title><link>https://breq.dev/projects/matrix4/</link><guid isPermaLink="true">https://breq.dev/projects/matrix4/</guid><description>Connecting my wall-mounted LED display to my smart home</description><pubDate>Wed, 03 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1920&quot; height=&quot;1440&quot; src=&quot;https://breq.dev/_astro/thumbnail.BdiGhVDV_Z1UxUhQ.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;My girlfriend &lt;a href=&quot;https://avasilver.dev/&quot;&gt;Ava&lt;/a&gt; and I recently extended the LED matrix we use to display weather and transit information to receive commands over the &lt;a href=&quot;https://en.wikipedia.org/wiki/Matter_(standard)&quot;&gt;Matter&lt;/a&gt; smart home protocol.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I have somewhat of an obsession with building interfaces out of HUB75 format LED matrices (the type often chained together to build large video walls). They can be obtained for relatively cheap and are easy to control, but are flexible enough to convey a huge amount of information. I love the vibrant look they have (those close to me will be very familiar with my love of bright colors), how visible they are even across the room, and how simplistic they feel compared to other screens in the home. They bring the helpful, pragmatic, unobtrusive vibe of subway countdown displays into my personal spaces.&lt;/p&gt;
&lt;p&gt;Simultaneously, because of polyamory, the room in which our display is mounted at home has begun to function as a secondary bedroom. The screen is bright enough that it has to be turned off at night. We realized it would be nice to tie this into our smart home automation as a Matter device, so we could use it with both the Google Home devices we use for voice control and the Home Assistant instance we use for home/away automation.&lt;/p&gt;
&lt;p&gt;The most obvious choice of protocol was Matter, since it didn’t require the Raspberry Pi powering the display to be equipped with any extra hardware and it would allow it to pair to both the Google Home cloud and to my Home Assistant installation simultaneously without any platform-specific code.&lt;/p&gt;
&lt;h1 id=&quot;setting-up-matter&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#setting-up-matter&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Setting up Matter&lt;/h1&gt;
&lt;p&gt;Ava did most of the work in this part!&lt;/p&gt;
&lt;p&gt;Most of the code for the LED matrix is written in Python. I originally chose Python as it made it easy to write and test additional “screens” for the display, and I could use the excellent &lt;a href=&quot;https://github.com/hzeller/rpi-rgb-led-matrix&quot;&gt;rpi-rgb-led-matrix&lt;/a&gt; library’s Python bindings to drive the panel.&lt;/p&gt;
&lt;p&gt;We looked for a good library that would implement the Matter protocol in Python but came up short. Adafruit’s &lt;a href=&quot;https://docs.circuitpython.org/projects/matter/en/latest/&quot;&gt;CircuitMatter&lt;/a&gt; was the strongest contender, but we found that it was unable to generate cryptographic keys during the pairing step within the time that Google Home allowed for pairing to take place.&lt;/p&gt;
&lt;p&gt;Knowing that we would need to bring in another language, we settled on the &lt;a href=&quot;https://github.com/matter-js/matter.js&quot;&gt;Matter.JS&lt;/a&gt; library. It’s what powers the Home Assistant Matter integration and worked great for pairing with both Google Home and Home Assistant. We were able to quickly modify the &lt;a href=&quot;https://github.com/matter-js/matter.js/blob/4cf4d4d4d7498644ed3e5b983b652551f8814282/examples/device-onoff/src/DeviceNode.ts&quot;&gt;matter.js On-Off Device example&lt;/a&gt; for our needs.&lt;/p&gt;
&lt;p&gt;We chose to expose the LED matrix as a “dimmable light,” allowing both its on/off status and its brightness to be controlled via the Matter interface. Here’s a simplified example of what that looks like in code!&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  DeviceTypeId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Endpoint&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Environment&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Logger&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  ServerNode&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  StorageService&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  Time&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  VendorId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@matter/main&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; DimmableLightDevice &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@matter/main/devices&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; uniqueId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// keep this in storage&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; server &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; ServerNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; uniqueId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;network&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5540&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;commissioning&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;passcode&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20202021&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// example values&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;discriminator&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3840&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;productDescription&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Wall LED Matrix&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;deviceType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;DeviceTypeId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;DimmableLightDevice&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deviceType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;basicInformation&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;vendorName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Oomfie Networks&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;vendorId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;VendorId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0xfff1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Development only VID&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;nodeLabel&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Wall LED Matrix&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;productName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Wall LED Matrix&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;productLabel&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Wall LED Matrix&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;productId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x8000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Development only PID&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;serialNumber&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;matterjs-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;uniqueId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    uniqueId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; endpoint &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;DimmableLightDevice&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dimmable&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;endpoint&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

endpoint&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onOff&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onOff$Changed&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;warn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Device is now &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;on&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;off&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

endpoint&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;levelControl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentLevel$Changed&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; percent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;254&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;warn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Brightness is now &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;percent&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ava looked into a few different ways to implement the communication between the JavaScript-based Matter client and the Python-based part of the project, such as FFI calls between Python and JavaScript, but we realized setting that up would add a lot of complexity. We eventually chose a &lt;a href=&quot;https://en.wikipedia.org/wiki/Unix_domain_socket&quot;&gt;Unix Domain Socket&lt;/a&gt; since it was by far the simplest option. The code uses a simple bespoke “on” / “off” / “set brightness” protocol. Importantly, commands need to flow both ways: if the user uses the dial on the LED matrix to adjust the brightness, that device state update needs to be reported back upstream to the Matter controllers.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1341&quot; height=&quot;926&quot; src=&quot;https://breq.dev/_astro/homeassistant.Lx8Tev7L_1sgmut.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Here’s a screenshot of how the device appears in Home Assistant. As you can see, it triggers nicely from our home and away automations, and it also responds nicely to “good morning” and “good night” voice commands via Google Home!&lt;/p&gt;
&lt;h1 id=&quot;other-small-upgrades&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#other-small-upgrades&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Other small upgrades&lt;/h1&gt;
&lt;p&gt;A long-overdue upgrade was redesigning the bottom piece of the enclosure to better support the back of the PCB and prevent the board from flexing with the dial button was pushed in. Previously, when pushing on the button, the board would flex out of the way first, making it unreliable and difficult to actually press down enough to register a click. Fixing this unfortunately required making two screws visible which were once contained internally, making the surface just a little bit less clean.&lt;/p&gt;
&lt;p&gt;It does, however, mean I get to play around with either changing the front panel color or leaving it off to expose the board behind it! I did end up wanting a cover to help reduce light leakage from the Raspberry Pi’s LEDs and make the project look a bit tidier, and since bare PCBs do look a bit out of place in home decor. I tried using translucent purple filament to make an interesting faceplate too, but the 2.5mm air gap between the plate and the PCB combined with the fine pattern on the PCB mean that it’s sort of just a uniform dark purple color. In the end I went back to the solid pink faceplate.&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1450&quot; height=&quot;1450&quot; src=&quot;https://breq.dev/_astro/new-cad.CwwDBddy_Z2fwQwx.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2484&quot; height=&quot;2484&quot; src=&quot;https://breq.dev/_astro/pink-faceplate.D6ajmEDD_1Cfhxc.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2360&quot; height=&quot;2360&quot; src=&quot;https://breq.dev/_astro/no-faceplate.BhNKVKQA_ZjAAfs.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2423&quot; height=&quot;2423&quot; src=&quot;https://breq.dev/_astro/purple-faceplate.4JREDPVm_ZvHDOA.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Even though the aesthetics are a bit compromised, the functionality is infinitely better, and the button click is much more reliable and satisfying.&lt;/p&gt;
&lt;p&gt;I also took the time to port the weather functionality from OpenWeatherMap to the Open-Meteo API, since they provided “high” and “low” temperatures and had the ability to request forecast data for free. I kept the current weather display the same, but added a screen to display the forecast of the next several days.&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2302&quot; height=&quot;2302&quot; src=&quot;https://breq.dev/_astro/forecast-64.C4He6iYS_24wKXr.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3072&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/forecast-32.lfVlPuo6_Z1nltVz.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h1 id=&quot;looking-ahead&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#looking-ahead&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Looking ahead&lt;/h1&gt;
&lt;p&gt;It is really nice to have the display automatically turn itself on in the morning and off at night.&lt;/p&gt;
&lt;p&gt;Working on projects with others is something that I find really fun and rewarding that I don’t often get to do anymore since graduating college! It’s been very fun to get Ava’s help and input. I think her architectural decisions made the code for this project way cleaner and more maintainable than what it would have been had I written it alone.&lt;/p&gt;
&lt;p&gt;I often feel like investing this level of effort into building devices and polished experiences that only a handful of people will ever use is a frivolous use of time. Continuing to iterate on this design, however, has started to show me that the act of creating something like this itself brings me a lot of joy! There’s also something special about working on something that I get to use every single day, and getting to enjoy the project I made over and over again.&lt;/p&gt;</content:encoded></item><item><title>How I became a domain registrar*</title><link>https://breq.dev/2026/05/19/registrar/</link><guid isPermaLink="true">https://breq.dev/2026/05/19/registrar/</guid><description>*but maybe not the TLDs you&apos;re thinking of</description><pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This website has the domain name &lt;code&gt;breq.dev&lt;/code&gt;. To set it up, I purchased it from a registrar (Porkbun), who then bought it at wholesale prices from the &lt;code&gt;.dev&lt;/code&gt; registry (run by Google).&lt;/p&gt;
&lt;p&gt;When you open this website in your browser, your computer &lt;em&gt;resolves&lt;/em&gt; this domain using the DNS protocol. Specifically, it looks up &lt;code&gt;.dev&lt;/code&gt; in the root servers to get Google’s servers, looks up &lt;code&gt;breq.dev&lt;/code&gt; in Google’s servers to get &lt;em&gt;my&lt;/em&gt; nameservers (run by Cloudflare in this case), and then queries those servers to get the IP address of the server hosting &lt;code&gt;breq.dev&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But who runs these root servers? Who decided that &lt;code&gt;.dev&lt;/code&gt; should be a domain name, or that Google should have the exclusive right to manage it? In practice, the root servers are managed by ICANN (the aptly-named Internet Corporation for Assigned Names and Numbers), which delegates top-level domains like &lt;code&gt;.dev&lt;/code&gt; to whoever pays them &lt;a href=&quot;https://www.icann.org/en/blogs/details/icann-sets-expected-evaluation-fee-for-new-gtld-applications-in-the-next-round-25-09-2024-en&quot;&gt;lots of money&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That said, there’s nothing inherent to the DNS protocol that specifies that ICANN runs the root servers or prevents anyone else from starting their own. This is where &lt;a href=&quot;https://diginoltar.nl/&quot;&gt;DigiNøltar&lt;/a&gt; comes in — it is an alternate set of DNS root servers for the Internet, with its own TLDs and its own rules for who gets to run them. For instance, DigiNøltar itself is available at &lt;code&gt;notar.øl&lt;/code&gt; (or more precisely, &lt;code&gt;notar.xn--l-4ga&lt;/code&gt; in &lt;a href=&quot;https://en.wikipedia.org/wiki/Internationalized_domain_name&quot;&gt;IDN normalized&lt;/a&gt; form). And this website is available at &lt;code&gt;brooke.oomf&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;This project involved setting up my own authoritative DNS server, which was much more complex than I anticipated! DNS is one of the protocols that most software developers are familiar with, but few ever interact with at such a low level.&lt;/p&gt;
&lt;h1 id=&quot;authoritative-and-recursive-nameservers&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#authoritative-and-recursive-nameservers&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Authoritative and Recursive Nameservers&lt;/h1&gt;
&lt;p&gt;The DNS system is built on “nameservers”: servers which receive requests for domain names and return corresponding DNS records. These servers are split into two types: “authoritative” and “recursive.”&lt;/p&gt;
&lt;p&gt;Recursive nameservers are the ones you’re likely most familiar with, like &lt;code&gt;8.8.8.8&lt;/code&gt; or &lt;code&gt;1.1.1.1&lt;/code&gt;. When a recursive nameserver receives a request for a domain name, it is responsible for contacting the appropriate authoritative nameservers until it finds the record it is looking for.&lt;/p&gt;
&lt;p&gt;Using the &lt;code&gt;dig&lt;/code&gt; command and the &lt;code&gt;+norecurse&lt;/code&gt; option, we can see this chain in action! To illustrate, let’s look up two domains that point to the same site: &lt;code&gt;diginoltar.nl&lt;/code&gt; using the ICANN root servers and &lt;code&gt;notar.xn--l-4ga&lt;/code&gt; using the DigiNøltar root servers.&lt;/p&gt;
&lt;p&gt;Let’s start with one of the ICANN root servers. Although they’re operated by different entities, they all should behave identically. There are not that many root servers, so their IP addresses are often hardcoded — let’s assume we know that the IP address of &lt;code&gt;a.root-servers.net&lt;/code&gt; is &lt;code&gt;198.41.0.4&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;mx-auto my-4 max-w-4xl overflow-clip rounded-2xl border-2 border-black bg-black font-body text-lg text-black&quot;&gt;&lt;h3 class=&quot;mx-4! my-2! font-mono! font-bold! text-white text-xl! not-italic! text-left!&quot;&gt;&lt;span class=&quot;text-panpink-light&quot;&gt;$ &lt;/span&gt;dig @198.41.0.4 +norecurse A diginoltar.nl&lt;/h3&gt;&lt;hr class=&quot;border border-black m-0!&quot;&gt;&lt;div class=&quot;bg-panblue-light px-4 py-2&quot;&gt;&lt;p class=&quot;mb-2! text-center font-mono italic&quot;&gt;Authority Section&lt;/p&gt;&lt;div class=&quot;overflow-x-auto&quot;&gt;&lt;table class=&quot;w-full mt-0!&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;TTL&lt;/th&gt;&lt;th&gt;Class&lt;/th&gt;&lt;th&gt;Type&lt;/th&gt;&lt;th&gt;Value&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr class=&quot;font-mono *:border-2 *:border-black *:px-2&quot;&gt;&lt;td&gt;nl.&lt;/td&gt;&lt;td&gt;172800&lt;/td&gt;&lt;td&gt;IN&lt;/td&gt;&lt;td&gt;NS&lt;/td&gt;&lt;td&gt;ns3.dns.nl.&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;font-mono *:border-2 *:border-black *:px-2&quot;&gt;&lt;td&gt;nl.&lt;/td&gt;&lt;td&gt;172800&lt;/td&gt;&lt;td&gt;IN&lt;/td&gt;&lt;td&gt;NS&lt;/td&gt;&lt;td&gt;ns1.dns.nl.&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;font-mono *:border-2 *:border-black *:px-2&quot;&gt;&lt;td&gt;nl.&lt;/td&gt;&lt;td&gt;172800&lt;/td&gt;&lt;td&gt;IN&lt;/td&gt;&lt;td&gt;NS&lt;/td&gt;&lt;td&gt;ns4.dns.nl.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;hr class=&quot;border border-black m-0!&quot;&gt;&lt;div class=&quot;bg-brookepurple-light px-4 py-2&quot;&gt;&lt;p class=&quot;mb-2! text-center font-mono italic&quot;&gt;Additional Section&lt;/p&gt;&lt;div class=&quot;overflow-x-auto&quot;&gt;&lt;table class=&quot;w-full mt-0!&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;TTL&lt;/th&gt;&lt;th&gt;Class&lt;/th&gt;&lt;th&gt;Type&lt;/th&gt;&lt;th&gt;Value&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr class=&quot;font-mono *:border-2 *:border-black *:px-2&quot;&gt;&lt;td&gt;ns3.dns.nl.&lt;/td&gt;&lt;td&gt;172800&lt;/td&gt;&lt;td&gt;IN&lt;/td&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;194.0.25.24&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;font-mono *:border-2 *:border-black *:px-2&quot;&gt;&lt;td&gt;ns3.dns.nl.&lt;/td&gt;&lt;td&gt;172800&lt;/td&gt;&lt;td&gt;IN&lt;/td&gt;&lt;td&gt;AAAA&lt;/td&gt;&lt;td&gt;2001:678:20::24&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;font-mono *:border-2 *:border-black *:px-2&quot;&gt;&lt;td&gt;ns1.dns.nl.&lt;/td&gt;&lt;td&gt;172800&lt;/td&gt;&lt;td&gt;IN&lt;/td&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;194.0.28.53&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;font-mono *:border-2 *:border-black *:px-2&quot;&gt;&lt;td&gt;ns1.dns.nl.&lt;/td&gt;&lt;td&gt;172800&lt;/td&gt;&lt;td&gt;IN&lt;/td&gt;&lt;td&gt;AAAA&lt;/td&gt;&lt;td&gt;2001:678:2c:0:194:0:28:53&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;font-mono *:border-2 *:border-black *:px-2&quot;&gt;&lt;td&gt;ns4.dns.nl.&lt;/td&gt;&lt;td&gt;172800&lt;/td&gt;&lt;td&gt;IN&lt;/td&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;185.159.199.200&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;font-mono *:border-2 *:border-black *:px-2&quot;&gt;&lt;td&gt;ns4.dns.nl.&lt;/td&gt;&lt;td&gt;172800&lt;/td&gt;&lt;td&gt;IN&lt;/td&gt;&lt;td&gt;AAAA&lt;/td&gt;&lt;td&gt;2620:10a:80ac::200&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Remember, it’s not the root server’s job to have records for every domain name — that’s why we don’t see &lt;code&gt;diginoltar.nl&lt;/code&gt; in the response at all! The root server points you to the authoritative server for the top level domain (&lt;code&gt;.nl&lt;/code&gt;), and that server points to the authoritative server for the second-level domain (&lt;code&gt;diginoltar.nl&lt;/code&gt;), and&lt;/p&gt;
&lt;p&gt;First, look at the Authority Section. This specifies what nameservers (“NS”) are responsible for the &lt;code&gt;.nl&lt;/code&gt; domain. We can keep querying any of &lt;code&gt;ns1&lt;/code&gt;, &lt;code&gt;ns3&lt;/code&gt;, or &lt;code&gt;ns4.dns.nl&lt;/code&gt; to find our answer.&lt;/p&gt;
&lt;p&gt;You might wonder: how can we query, say, &lt;code&gt;ns1.dns.nl&lt;/code&gt; to look up records if we don’t know its IP address? Since it’s under the &lt;code&gt;.nl&lt;/code&gt; zone, we’d need to query &lt;code&gt;ns1.dns.nl&lt;/code&gt; to figure out — it’s a chicken-and-egg problem. To get around this, the root server also supplied “glue records” in the Additional Section: the “A” and “AAAA” records mapping &lt;code&gt;ns1.dns.nl&lt;/code&gt; and the others to IPv4 and IPv6 addresses we can connect to. (The IPv6 ones are called AAAA records since an IPv6 address is four times as many bytes as an IPv4 address!)&lt;/p&gt;
&lt;p&gt;Cool, now let’s pick one of those addresses at random and ask it for diginoltar.nl:&lt;/p&gt;
&lt;div class=&quot;mx-auto my-4 max-w-4xl overflow-clip rounded-2xl border-2 border-black bg-black font-body text-lg text-black&quot;&gt;&lt;h3 class=&quot;mx-4! my-2! font-mono! font-bold! text-white text-xl! not-italic! text-left!&quot;&gt;&lt;span class=&quot;text-panpink-light&quot;&gt;$ &lt;/span&gt;dig @194.0.25.24 +norecurse A diginoltar.nl&lt;/h3&gt;&lt;hr class=&quot;border border-black m-0!&quot;&gt;&lt;div class=&quot;bg-panblue-light px-4 py-2&quot;&gt;&lt;p class=&quot;mb-2! text-center font-mono italic&quot;&gt;Authority Section&lt;/p&gt;&lt;div class=&quot;overflow-x-auto&quot;&gt;&lt;table class=&quot;w-full mt-0!&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;TTL&lt;/th&gt;&lt;th&gt;Class&lt;/th&gt;&lt;th&gt;Type&lt;/th&gt;&lt;th&gt;Value&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr class=&quot;font-mono *:border-2 *:border-black *:px-2&quot;&gt;&lt;td&gt;diginoltar.nl.&lt;/td&gt;&lt;td&gt;3600&lt;/td&gt;&lt;td&gt;IN&lt;/td&gt;&lt;td&gt;NS&lt;/td&gt;&lt;td&gt;ns1.famfo.xyz.&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;font-mono *:border-2 *:border-black *:px-2&quot;&gt;&lt;td&gt;diginoltar.nl.&lt;/td&gt;&lt;td&gt;3600&lt;/td&gt;&lt;td&gt;IN&lt;/td&gt;&lt;td&gt;NS&lt;/td&gt;&lt;td&gt;ns1.homecloud.lol.&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;font-mono *:border-2 *:border-black *:px-2&quot;&gt;&lt;td&gt;diginoltar.nl.&lt;/td&gt;&lt;td&gt;3600&lt;/td&gt;&lt;td&gt;IN&lt;/td&gt;&lt;td&gt;NS&lt;/td&gt;&lt;td&gt;miyuki.sakamoto.pl.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Awesome — we now have some servers which are authoritative for &lt;code&gt;diginoltar.nl&lt;/code&gt;! Notice that we don’t need glue records this time, since they’re all on a different domain than &lt;code&gt;.nl&lt;/code&gt;. We can look up the IP address of the first one using the same process we’re doing now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ask the root server: &lt;code&gt;dig +norecurse A @198.41.0.4 ns1.famfo.xyz&lt;/code&gt; gives us the &lt;code&gt;.xyz&lt;/code&gt; nameserver &lt;code&gt;x.nic.xyz&lt;/code&gt;, with a glue record showing it at &lt;code&gt;194.169.218.42&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Ask the authoritative &lt;code&gt;.xyz&lt;/code&gt; server: &lt;code&gt;dig +norecurse A @194.169.218.42 ns1.famfo.xyz&lt;/code&gt; gives us the &lt;code&gt;.famfo.xyz&lt;/code&gt; nameserver &lt;code&gt;ns1.famfo.xyz&lt;/code&gt;, with a glue record showing it at &lt;code&gt;116.202.10.127&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Ask the authoritative &lt;code&gt;famfo.xyz&lt;/code&gt; server: &lt;code&gt;dig +norecurse A @116.202.10.127 ns1.famfo.xyz&lt;/code&gt; gives us &lt;code&gt;116.202.10.127&lt;/code&gt;!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You might have noticed that we can skip the last step, but I wrote it out for completeness.&lt;/p&gt;
&lt;p&gt;Finally, we can ask &lt;code&gt;ns1.famfo.xyz&lt;/code&gt; for &lt;code&gt;diginoltar.nl&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;mx-auto my-4 max-w-4xl overflow-clip rounded-2xl border-2 border-black bg-black font-body text-lg text-black&quot;&gt;&lt;h3 class=&quot;mx-4! my-2! font-mono! font-bold! text-white text-xl! not-italic! text-left!&quot;&gt;&lt;span class=&quot;text-panpink-light&quot;&gt;$ &lt;/span&gt;dig @116.202.10.127 +norecurse A diginoltar.nl&lt;/h3&gt;&lt;hr class=&quot;border border-black m-0!&quot;&gt;&lt;div class=&quot;bg-panyellow-light px-4 py-2&quot;&gt;&lt;p class=&quot;mb-2! text-center font-mono italic&quot;&gt;Answer Section&lt;/p&gt;&lt;div class=&quot;overflow-x-auto&quot;&gt;&lt;table class=&quot;w-full mt-0!&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;TTL&lt;/th&gt;&lt;th&gt;Class&lt;/th&gt;&lt;th&gt;Type&lt;/th&gt;&lt;th&gt;Value&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr class=&quot;font-mono *:border-2 *:border-black *:px-2&quot;&gt;&lt;td&gt;diginoltar.nl.&lt;/td&gt;&lt;td&gt;3600&lt;/td&gt;&lt;td&gt;IN&lt;/td&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;5.75.163.176&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And we get our answer: &lt;code&gt;5.75.163.176&lt;/code&gt;! This is the website hosting &lt;code&gt;diginoltar.nl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, let’s repeat this process, but with the DigiNøltar infrastructure and the &lt;code&gt;notar.xn--l-4ga&lt;/code&gt; domain name.&lt;/p&gt;
&lt;p&gt;Ask the root server, &lt;code&gt;51.195.3.249&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;mx-auto my-4 max-w-4xl overflow-clip rounded-2xl border-2 border-black bg-black font-body text-lg text-black&quot;&gt;&lt;h3 class=&quot;mx-4! my-2! font-mono! font-bold! text-white text-xl! not-italic! text-left!&quot;&gt;&lt;span class=&quot;text-panpink-light&quot;&gt;$ &lt;/span&gt;dig @51.195.3.249 +norecurse A notar.xn--l-4ga&lt;/h3&gt;&lt;hr class=&quot;border border-black m-0!&quot;&gt;&lt;div class=&quot;bg-panblue-light px-4 py-2&quot;&gt;&lt;p class=&quot;mb-2! text-center font-mono italic&quot;&gt;Authority Section&lt;/p&gt;&lt;div class=&quot;overflow-x-auto&quot;&gt;&lt;table class=&quot;w-full mt-0!&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;TTL&lt;/th&gt;&lt;th&gt;Class&lt;/th&gt;&lt;th&gt;Type&lt;/th&gt;&lt;th&gt;Value&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr class=&quot;font-mono *:border-2 *:border-black *:px-2&quot;&gt;&lt;td&gt;øl.&lt;/td&gt;&lt;td&gt;600&lt;/td&gt;&lt;td&gt;IN&lt;/td&gt;&lt;td&gt;NS&lt;/td&gt;&lt;td&gt;ns1.øl.gtld-servers.ol.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Cool, we need to find &lt;code&gt;ns1.øl.gtld-servers.ol&lt;/code&gt;. Let’s ask the root nameserver again:&lt;/p&gt;
&lt;div class=&quot;mx-auto my-4 max-w-4xl overflow-clip rounded-2xl border-2 border-black bg-black font-body text-lg text-black&quot;&gt;&lt;h3 class=&quot;mx-4! my-2! font-mono! font-bold! text-white text-xl! not-italic! text-left!&quot;&gt;&lt;span class=&quot;text-panpink-light&quot;&gt;$ &lt;/span&gt;dig @51.195.3.249 +norecurse A ns1.øl.gtld-servers.ol&lt;/h3&gt;&lt;hr class=&quot;border border-black m-0!&quot;&gt;&lt;div class=&quot;bg-panyellow-light px-4 py-2&quot;&gt;&lt;p class=&quot;mb-2! text-center font-mono italic&quot;&gt;Answer Section&lt;/p&gt;&lt;div class=&quot;overflow-x-auto&quot;&gt;&lt;table class=&quot;w-full mt-0!&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;TTL&lt;/th&gt;&lt;th&gt;Class&lt;/th&gt;&lt;th&gt;Type&lt;/th&gt;&lt;th&gt;Value&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr class=&quot;font-mono *:border-2 *:border-black *:px-2&quot;&gt;&lt;td&gt;ns1.øl.gtld-servers.ol.&lt;/td&gt;&lt;td&gt;600&lt;/td&gt;&lt;td&gt;IN&lt;/td&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;5.75.163.176&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We get an A record response directly — this is uncommon but allowed within the protocol. Let’s ask it about &lt;code&gt;notar.xn--l-4ga&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;mx-auto my-4 max-w-4xl overflow-clip rounded-2xl border-2 border-black bg-black font-body text-lg text-black&quot;&gt;&lt;h3 class=&quot;mx-4! my-2! font-mono! font-bold! text-white text-xl! not-italic! text-left!&quot;&gt;&lt;span class=&quot;text-panpink-light&quot;&gt;$ &lt;/span&gt;dig @5.75.163.176 +norecurse A notar.xn--l-4ga&lt;/h3&gt;&lt;hr class=&quot;border border-black m-0!&quot;&gt;&lt;div class=&quot;bg-panyellow-light px-4 py-2&quot;&gt;&lt;p class=&quot;mb-2! text-center font-mono italic&quot;&gt;Answer Section&lt;/p&gt;&lt;div class=&quot;overflow-x-auto&quot;&gt;&lt;table class=&quot;w-full mt-0!&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;TTL&lt;/th&gt;&lt;th&gt;Class&lt;/th&gt;&lt;th&gt;Type&lt;/th&gt;&lt;th&gt;Value&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr class=&quot;font-mono *:border-2 *:border-black *:px-2&quot;&gt;&lt;td&gt;notar.øl.&lt;/td&gt;&lt;td&gt;3600&lt;/td&gt;&lt;td&gt;IN&lt;/td&gt;&lt;td&gt;A&lt;/td&gt;&lt;td&gt;5.75.163.176&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Awesome, another A record! We now know that &lt;code&gt;notar.øl&lt;/code&gt; is hosted on &lt;code&gt;5.75.163.176&lt;/code&gt;, the same server as &lt;code&gt;diginoltar.nl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can see how this is, essentially, two parallel sets of infrastructure, coexisting on the public Internet. Just as I can set my router to use a recursive resolver like &lt;code&gt;8.8.8.8&lt;/code&gt; that uses the ICANN root servers, I can set up my router to use the &lt;a href=&quot;http://diginoltar.nl/uitleg-root/&quot;&gt;DigiNøltar recursive resolver&lt;/a&gt; at &lt;code&gt;51.195.8.10&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id=&quot;hosting-a-nameserver&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#hosting-a-nameserver&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Hosting a nameserver&lt;/h1&gt;
&lt;p&gt;Suppose we want to create our own TLD, &lt;code&gt;.oomf&lt;/code&gt;. We need to host an authoritative nameserver for it — something that DigiNøltar’s root servers will give to people when they ask about &lt;code&gt;.oomf&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/BIND&quot;&gt;BIND&lt;/a&gt; is the most common software used for this type of thing. We need to set up a maze of configuration files on disk in order to use it.&lt;/p&gt;
&lt;p&gt;First, let’s set up BIND’s root configuration file. This sets the path of our other files. &lt;code&gt;recursion no&lt;/code&gt; specifies that this is an authoritative server, and shouldn’t try to resolve stuff for clients like a recursive server would.&lt;/p&gt;
&lt;pre class=&quot;language-c&quot; data-language=&quot;c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// /etc/bind/named.conf.options&lt;/span&gt;
options &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    directory &lt;span class=&quot;token string&quot;&gt;&amp;quot;/var/cache/bind&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    listen&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;on &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; any&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    listen&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;on&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;v6 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; any&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    allow&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;query &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; any&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    recursion no&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    dnssec&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;validation no&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Awesome — let’s declare our zone! Here’s where we would put all of the TLDs we want to be authoritative for. Right now, it’s just &lt;code&gt;.oomf&lt;/code&gt;. &lt;code&gt;type primary&lt;/code&gt; means that we are the authoriative source for this zone — &lt;code&gt;secondary&lt;/code&gt; would be for load-balancing or failover servers. Each zone gets its own &lt;em&gt;zone file&lt;/em&gt;, specified with the &lt;code&gt;file&lt;/code&gt; keyword.&lt;/p&gt;
&lt;pre class=&quot;language-c&quot; data-language=&quot;c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// /var/cache/bind/named.conf.local&lt;/span&gt;
zone &lt;span class=&quot;token string&quot;&gt;&amp;quot;oomf&amp;quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    type primary&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    file &lt;span class=&quot;token string&quot;&gt;&amp;quot;/etc/bind/zones/db.oomf&amp;quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s put in our zone definition! Here’s where we switch from the C-style syntax of BIND configuration files into the format of &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc1035#section-5&quot;&gt;DNS zone files&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-dns-zone-file&quot; data-language=&quot;dns-zone-file&quot;&gt;&lt;code class=&quot;language-dns-zone-file&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;; /etc/bind/zones/db.oomf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;$ORIGIN&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;oomf.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;$TTL&lt;/span&gt; 3600

&lt;span class=&quot;token variable&quot;&gt;@&lt;/span&gt; &lt;span class=&quot;token class keyword&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;token type keyword&quot;&gt;SOA&lt;/span&gt; ns1.oomf.gtld-servers.ol. hostmaster.oomf. &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    2026050701 &lt;span class=&quot;token comment&quot;&gt;; serial&lt;/span&gt;
    3600       &lt;span class=&quot;token comment&quot;&gt;; refresh&lt;/span&gt;
    60         &lt;span class=&quot;token comment&quot;&gt;; retry&lt;/span&gt;
    604800     &lt;span class=&quot;token comment&quot;&gt;; expire&lt;/span&gt;
    60         &lt;span class=&quot;token comment&quot;&gt;; minimum TTL&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;@&lt;/span&gt; &lt;span class=&quot;token class keyword&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;token type keyword&quot;&gt;NS&lt;/span&gt; ns1.oomf.gtld-servers.ol.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;DigiNøltar will point &lt;code&gt;ns1.oomf.gtld-servers.ol&lt;/code&gt; at us. &lt;code&gt;SOA&lt;/code&gt; is “Start of Authority”, it declares that this server is the authority for &lt;code&gt;oomf&lt;/code&gt; (which is represented by the &lt;code&gt;@&lt;/code&gt; symbol). The NS record is what will actually tell recursive resolvers who is the authority for this domain — it’s where we’d put alternate server names like &lt;code&gt;ns2.oomf.gtld-servers.ol&lt;/code&gt; if we had more than one.&lt;/p&gt;
&lt;p&gt;At this point, we’re all ready to start issuing domains! Let’s use &lt;code&gt;brooke.oomf&lt;/code&gt; as an example.&lt;/p&gt;
&lt;p&gt;If we want to also host DNS records for domains, we do it like this. First, declare that the zone exists:&lt;/p&gt;
&lt;pre class=&quot;language-dns-zone-file&quot; data-language=&quot;dns-zone-file&quot;&gt;&lt;code class=&quot;language-dns-zone-file&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;; /etc/bind/zones/db.oomf&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;; [...]&lt;/span&gt;
brooke    &lt;span class=&quot;token class keyword&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;token type keyword&quot;&gt;NS&lt;/span&gt; ns1.oomf.gtld-servers.ol.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, include the relevant records — the &lt;code&gt;SOA&lt;/code&gt; declaring that we’re the authority, the relevant NS, and finally the A specifying where the web server is:&lt;/p&gt;
&lt;pre class=&quot;language-dns-zone-file&quot; data-language=&quot;dns-zone-file&quot;&gt;&lt;code class=&quot;language-dns-zone-file&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;; /etc/bind/zones/db.brooke.oomf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;$ORIGIN&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;brooke.oomf.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;$TTL&lt;/span&gt; 3600

&lt;span class=&quot;token variable&quot;&gt;@&lt;/span&gt; &lt;span class=&quot;token class keyword&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;token type keyword&quot;&gt;SOA&lt;/span&gt; ns1.oomf.gtld-servers.ol. hostmaster.oomf. &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    2026050701 &lt;span class=&quot;token comment&quot;&gt;; serial&lt;/span&gt;
    3600
    60
    604800
    60
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;@&lt;/span&gt; &lt;span class=&quot;token class keyword&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;token type keyword&quot;&gt;NS&lt;/span&gt;  ns1.oomf.gtld-servers.ol.
&lt;span class=&quot;token variable&quot;&gt;@&lt;/span&gt; &lt;span class=&quot;token class keyword&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;token type keyword&quot;&gt;A&lt;/span&gt;   71.232.183.79
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we wanted to use a different authoritative server for the &lt;code&gt;brooke.oomf&lt;/code&gt; zone, for instance at &lt;code&gt;ns1.brooke.oomf&lt;/code&gt;, we’d just have to point at their nameserver, and include a glue record if needed:&lt;/p&gt;
&lt;pre class=&quot;language-dns-zone-file&quot; data-language=&quot;dns-zone-file&quot;&gt;&lt;code class=&quot;language-dns-zone-file&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;; /etc/bind/zones/db.oomf&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;; [...]&lt;/span&gt;
brooke           &lt;span class=&quot;token class keyword&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;token type keyword&quot;&gt;NS&lt;/span&gt; ns1.brooke.oomf.
ns1.brooke.oomf. &lt;span class=&quot;token class keyword&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;token type keyword&quot;&gt;A&lt;/span&gt;  71.232.183.79
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that’s it — our very own top-level domain name!&lt;/p&gt;
&lt;h1 id=&quot;how-can-you-get-in-on-this&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#how-can-you-get-in-on-this&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;How can you get in on this?&lt;/h1&gt;
&lt;p&gt;First off, you can use DigiNøltar’s DNS servers to access sites hosted on their TLDs! The easiest way is to set your DNS server to one of their &lt;a href=&quot;http://diginoltar.nl/uitleg-root/&quot;&gt;public recursive ones&lt;/a&gt;. In my case, since I live in the US and don’t want all of my DNS traffic to have to go to Europe and back, I just have overrides set up in my network’s recursive resolver to only send DNS requests for DigiNøltar TLDs to them and route ICANN TLDs to the normal root zone.&lt;/p&gt;
&lt;p&gt;You can also register a domain name from &lt;a href=&quot;http://notar.%C3%B8l/producten/&quot;&gt;any of the supported TLDs&lt;/a&gt;, usually by contacting that TLD’s registrar through some esoteric and fun method.&lt;/p&gt;
&lt;p&gt;And if you want — hosting your own TLD is an easy, fun, and free way to learn more about how DNS infrastructure works and is deployed in practice!&lt;/p&gt;</content:encoded></item><item><title>Laser Vector Projector</title><link>https://breq.dev/projects/laser-projector/</link><guid isPermaLink="true">https://breq.dev/projects/laser-projector/</guid><description>Vector graphics using fast mirrors, on the cheap!</description><pubDate>Sun, 03 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3681&quot; height=&quot;2454&quot; src=&quot;https://breq.dev/_astro/cover.8-_a3_ql_Z15FuR.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;My girlfriend &lt;a href=&quot;https://miakizz.quest/&quot;&gt;Mia&lt;/a&gt; and I built an RGB laser projector that uses a pair of galvanometers to direct the beam. Firmware running on an ESP32 can project a variety of vector graphics demos, Wii Nunchucks can be connected for interactive games, and a 3.5mm audio input allows using the projector as a music visualizer.&lt;/p&gt;
&lt;div class=&quot;callout-amber&quot;&gt;
&lt;h2 class=&quot;font-bold text-2xl&quot;&gt;Sponsored by PCBWay&lt;/h2&gt;
&lt;p&gt;This article is sponsored by &lt;a href=&quot;https://pcbway.com/g/245M3j&quot;&gt;PCBWay&lt;/a&gt;, who generously provided PCB fabrication services for this project. PCBWay provides high-quality boards with fast turnaround at low cost, and are a great option for both hobbyists and professionals.&lt;/p&gt;
&lt;/div&gt;
&lt;h1 id=&quot;galvanometers-101&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#galvanometers-101&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Galvanometers 101&lt;/h1&gt;
&lt;p&gt;Projection is a useful technique for displaying large images: instead of having to manufacture larger and larger displays, an image can be generated at any size, then expanded using optics to be shown on any surface available. The only limiting factor of size (up to a point) is the brightness of the light generating the image. Lasers are a great way to generate a lot of light in a small volume, so they are often used in projection applications. However, unless you want to just watch a static spot on the wall, it needs to be controlled.&lt;/p&gt;
&lt;p&gt;One way (simpler in theory, harder in practice) is to use a digital micromirror device (DMD) — a MEMS device made up of a grid of tiny mirrors that flip between two states. DMDs are effectively synonymous with DLP (Digital Light Processing), the Texas Instruments product line which holds a monopoly on this category of device. DMDs are generally difficult and expensive to acquire for hobbyist use, except through buying and disassembling commercially available video projectors — but these integrated systems limit a tinkerer’s level of control over the DMD chip. DMDs are also difficult to illuminate and inefficient at displaying “sparse” images.&lt;/p&gt;
&lt;p&gt;Another way is to quickly steer the beam to trace out the ideal shape. While moving the laser diode itself is tricky, moving mirrors to steer the beam can be done extremely fast. Laser projectors use a pair of mirrors mounted on &lt;em&gt;galvanometers&lt;/em&gt; (or “galvos” for short). Unlike conventional types of motors, these use the same mechanism as you’d find in an analog voltmeter to deflect the mirror in response to a current. They can’t move much mass, but they can respond very quickly to changes in input, making them perfect for this type of beam steering application.&lt;/p&gt;
&lt;p&gt;While a DMD allows you to draw raster images, galvos draw vector images — enabling a lot of fun applications that aren’t possible on typical consumer displays. Vector displays excel at creating sharp, smooth lines and polygons at any angle, however they are rarely used now due to raster being more practical for most applications. Ahoy’s &lt;a href=&quot;https://youtu.be/QyjyWUrHsFc?t=188&quot;&gt;A Brief History of Graphics&lt;/a&gt; has a section on vector graphics with renderings and examples which showcase this excellently.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1890&quot; height=&quot;714&quot; src=&quot;https://breq.dev/_astro/galvo-listing.DnnJVPfs_ZjueXe.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.aliexpress.us/item/3256809659931894.html&quot;&gt;X/Y galvo setup I bought&lt;/a&gt; came with two galvos, the X/Y bracket which connects them, a driver board, and a split-rail power supply. It takes in ±10V differential analog signals for X and Y position (likely corresponding to a &lt;a href=&quot;https://www.ilda.com/resources/StandardsDocs/ILDA_ISP99_rev002.pdf&quot;&gt;standard&lt;/a&gt; set by ILDA, the International Laser Display Association).&lt;/p&gt;
&lt;p&gt;Galvos for laser applications are often rated in “Kpps”, or “kilo-points per second,” which refers to the rate at which the galvos can display the ILDA standard test pattern at 8 degrees of deflection. My galvos are rated for 20 Kpps, which is on the slower side. Note that some manufacturers often lie about this measurement given its imprecise nature.&lt;/p&gt;
&lt;h1 id=&quot;red-green-and-blue-lasers&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#red-green-and-blue-lasers&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Red, green, and blue lasers&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1926&quot; height=&quot;772&quot; src=&quot;https://breq.dev/_astro/laser-listing.CIHH1ZwY_yhexC.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Lasers come in a variety of wavelengths dependent on the lasing medium. Some wavelengths are dramatically easier to make than others. Thankfully, lasers in the red, green, and blue ranges are pretty straightforward. Modules like &lt;a href=&quot;https://www.aliexpress.us/item/3256810125144918.html&quot;&gt;the one I purchased&lt;/a&gt; combine three wavelengths with &lt;em&gt;dichroic mirrors&lt;/em&gt; — filters that act like a mirror to some wavelengths, but transmit others through — to form a single beam.&lt;/p&gt;
&lt;div class=&quot;callout-red&quot;&gt;
&lt;h2 class=&quot;font-bold text-2xl mt-4&quot;&gt;Safety: Don&apos;t do this at home&lt;/h2&gt;
&lt;p&gt;Lasers are dangerous tools, and this isn’t the type of project you should attempt without a clear understanding of the risks. But since I know that might not stop you, here’s the &lt;em&gt;very basics&lt;/em&gt; of what you need to know to mess around with this stuff safely.&lt;/p&gt;
&lt;p&gt;Lasers are divided into classes, where a higher class number means higher risks. Here’s a table summarizing the classes for &lt;em&gt;visible light&lt;/em&gt; lasers — infrared or ultraviolet lasers bring additional risks of unintentional long-duration exposure as the beam can’t be seen.&lt;/p&gt;









































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Class&lt;/th&gt;&lt;th&gt;Power&lt;/th&gt;&lt;th&gt;Risks&lt;/th&gt;&lt;th&gt;Examples&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;I&lt;/td&gt;&lt;td&gt;&amp;#x3C;0.39 mW&lt;/td&gt;&lt;td&gt;Viewing through optical instruments&lt;/td&gt;&lt;td&gt;CD players&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;II&lt;/td&gt;&lt;td&gt;&amp;#x3C;1 mW&lt;/td&gt;&lt;td&gt;Staring directly into beam&lt;/td&gt;&lt;td&gt;Barcode readers, laser tag&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;IIIa&lt;/td&gt;&lt;td&gt;&amp;#x3C;5 mW&lt;/td&gt;&lt;td&gt;Briefly looking into beam&lt;/td&gt;&lt;td&gt;Laser pointers&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;IIIb&lt;/td&gt;&lt;td&gt;&amp;#x3C;500 mW&lt;/td&gt;&lt;td&gt;Eye or skin exposure to beam or specular reflections&lt;/td&gt;&lt;td&gt;Laser shows, industrial, research&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;IV&lt;/td&gt;&lt;td&gt;&gt;500 mW&lt;/td&gt;&lt;td&gt;Eye or skin exposure to beam or diffuse reflections&lt;/td&gt;&lt;td&gt;Laser shows, industrial, research, medical&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&quot;caption&quot;&gt;
&lt;p&gt;sources: &lt;a href=&quot;https://www.lasersafetyfacts.com/&quot;&gt;lasersafetyfacts.com&lt;/a&gt; and &lt;a href=&quot;https://www.fda.gov/radiation-emitting-products/home-business-and-entertainment-products/laser-products-and-instruments&quot;&gt;fda.gov&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Note that lasers bought from sketchy vendors are often mislabeled! This especially applies to laser pointers, which are required to be at most class IIIa. Several laser pointers I have purchased had “&amp;#x3C;5mW” printed on the outside but measured closer to 50 mW, well into class IIIb territory.&lt;/p&gt;
&lt;p&gt;Most laser safety regulations for visible lasers are based around a 0.25 second exposure time, with the rationale being that the human blink reflex would protect you from exposures longer than that time. Therefore, if a laser is categorized as dangerous for eye exposure, it means that &lt;em&gt;it can cause permanent damage faster than you can blink&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I work with very high power lasers at my day job and thus am familiar with the controls that a professional lab uses to ensure laser safety. I would not have had the confidence to attempt this project without that experience.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;One of the perks of working in perceptual color is, since human eyes have three types of color sensing cells, you only ever need three wavelengths. Red lasers are commonly found at the 638nm wavelength, so that one’s easy. There are lot of available blue wavelengths between about 440 and 460nm to choose from. Green is where it gets difficult.&lt;/p&gt;
&lt;p&gt;By far the most common green light wavelength is 532nm — it can be created easily by taking an infrared laser at 1064nm and passing its light through a frequency-doubling crystal to produce 532nm. However, this process is imperfect and cheap green lasers often lack the filtering required at the infrared wavelengths, creating a potential hazard if the user is not expecting to need protection against 1064nm light. For lower-power applications like this project, 520nm diodes have started to become available which directly produce light at the desired wavelength. And as another benefit, 520nm diodes help give you a larger color gamut! By mixing various amounts of red, green, and blue at those selected wavelengths, you can create any color in the triangle below.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;476&quot; height=&quot;540&quot; src=&quot;https://breq.dev/_astro/chromaticity.DqPVH8wx_Z3HVLi.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;caption&quot;&gt;
&lt;p&gt;Original source: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:CIExy1931_fixed.svg&quot;&gt;Wikimedia Commons&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;While I certainly can’t mention everything you should know regarding safety, one interesting note that ties closely into wavelength selection is laser safety goggles. Laser safety goggles have the difficult job of letting through enough ambient light for you to work while only letting through a tiny fraction of the light at the wavelength of your laser.&lt;/p&gt;
&lt;p&gt;As such, you must choose goggles which match the wavelengths you are using, and there are hundreds of different options for different applications.&lt;/p&gt;
&lt;p&gt;A lot of the aspects of this project revolved around buying parts as cheaply as possible to demonstrate how accessible experimenting with lasers is. Do not attempt to apply this mentality to safety equipment. By far the most expensive part of this project was purchasing laser safety goggles from a reputable, CE certified, ANSI-compliant vendor.&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3072&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/goggles.XTDXtQAG_Z1K2nDh.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2308&quot; height=&quot;2308&quot; src=&quot;https://breq.dev/_astro/goggles-blocking-light.DZcS99wF_2s7w6K.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The two numbers to look at are OD (Optical Density) at your desired wavelengths, and VLT (Visible Light Transmission) overall. You want a high enough OD to block harmful laser light, but a high enough VLT to allow you to see your surroundings comfortably. I got the &lt;a href=&quot;https://lasersafety.com/product/f42-p5l13-5000/&quot;&gt;F42.P5L13.5000&lt;/a&gt; from Laservision as they are rated for about OD 3 (allowing through only &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mrow&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msup&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{1}{10^3}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1901em;vertical-align:-0.345em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em;&quot;&gt;&lt;span style=&quot;top:-2.655em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7463em;&quot;&gt;&lt;span style=&quot;top:-2.786em;margin-right:0.0714em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.5em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size3 size1 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.345em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; of light) at each of the wavelengths in my laser module while having 20% VLT (allowing through 20% of all visible light).&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1139&quot; height=&quot;717&quot; src=&quot;https://breq.dev/_astro/goggles-rating.Bdh103lN_Z1Q97yM.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;initial-prototyping&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#initial-prototyping&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Initial prototyping&lt;/h1&gt;
&lt;p&gt;To paint vector art, two things are essential: precise control over the beam position, and precise control of the beam brightness/color.&lt;/p&gt;
&lt;p&gt;To control the beam position, we need a DAC to generate the analog signals corresponding to mirror positions. Although DACs are relatively common in industry, they are unfortunately absent from many hobbyist boards. The ATSAMD line common in MicroPython-focused boards has only a single DAC output. STM32s often have two DACs, but they aren’t as common on development boards outside of the Nucleo line (which are physically quite large), and the LQFP packages they come in are difficult enough to hand-solder that I would be wary of using a bare chip.&lt;/p&gt;
&lt;p&gt;Two variants of the ESP32 chip, however, do support DACs: the original ESP32 and the ESP32-S2 both have two 8-bit DAC outputs. 8 bit outputs essentially give a 256x256 grid for outputs, which is quite small by professional standards but still pretty good for this project. Additionally, the galvos themselves have some inertia, which has a smoothing effect on diagonal lines and hides the “stair step” phenomena in the output. I chose the ESP32-S2 as it was the newer chip, although it does not support Bluetooth.&lt;/p&gt;
&lt;p&gt;I chose the ESP32-S2 for this project because it has a built-in dual-channel DAC output. Having a DAC integrated into the microcontroller is useful not just to reduce complexity, but also to ensure the DAC can output samples quickly enough to move the beam at the desired rate. (If the galvos are rated for 20 Kpps, then the DAC should have at least a 20 kHz output rate!)&lt;/p&gt;
&lt;p&gt;Once the beam position is set, the next step is to control the brightness of each laser to produce arbitrary colors. While our laser controller only accepts a digital input to turn the beam on and off, we can still get a dimmable brightness effect using &lt;em&gt;pulse width modulation&lt;/em&gt; — output pulses at a fixed, very fast rate, and adjust the width of each pulse proportionally to the desired brightness.&lt;/p&gt;
&lt;p&gt;Unlike most microcontrollers, the ESP32 has two built-in PWM peripherals: the “LED Controller” and the “Motor Control Pulse Width Modulator” (MCPWM).&lt;/p&gt;
&lt;p&gt;The LED Controller is designed primarily to drive LEDs with PWM signals. As such, it lacks a lot of the features one might expect (the output frequency is fixed, for instance), but gains other abilities: the peripheral can automatically ramp between colors smoothly without involvement from the CPU. This is the peripheral I’m using for this project.&lt;/p&gt;
&lt;p&gt;The MCPWM gives additional options for timers, interrupts, and synchronizing with external systems — features that aren’t needed for this project.&lt;/p&gt;
&lt;p&gt;You might wonder why we can’t use PWM to steer the galvos, avoiding the need for a DAC altogether. However, unless the PWM frequency is extremely high, the galvo mirror would end up oscillating between the two extremes of its travel as the pulses switch between on and off instead of remaining steady at the desired position. The laser diodes have a longer response time, and they only need to do color changes on a longer timescale, like drawing a slow-changing gradient or changing the beam color in between drawing line segments.&lt;/p&gt;
&lt;p&gt;After choosing the microcontroller, I bought some breakout boards for prototyping. I started off using the &lt;a href=&quot;https://www.adafruit.com/product/5325&quot;&gt;QT Py ESP32&lt;/a&gt;, but Adafruit inexplicably decided not to break out the hardware UART pins on the chip, and the Rust toolchain for the ESP32-S2 doesn’t support panic traces or debug logs over the chip’s native USB, so I quickly switched to the &lt;a href=&quot;https://www.adafruit.com/product/5029&quot;&gt;TinyS2&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2973&quot; height=&quot;1982&quot; src=&quot;https://breq.dev/_astro/prototype.BKRX7gqI_Z1ElAS4.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The prototype setup certainly wasn’t polished, but it was a great way for me to validate the overall design.&lt;/p&gt;
&lt;h1 id=&quot;schematic-and-pcb-design&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#schematic-and-pcb-design&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Schematic and PCB Design&lt;/h1&gt;
&lt;p&gt;With the components vaguely in place, it was time to develop a PCB to organize and contain them all. I enlisted my lovely girlfriend &lt;a href=&quot;https://miakizz.quest/&quot;&gt;Mia&lt;/a&gt;, who is much more knowledgeable about this stuff than me, to help me with both the schematic and the board layout. This was my first time attempting a schematic of this complexity! Overall I’m quite happy with how much worked right away.&lt;/p&gt;
&lt;p&gt;After designing the board in KiCAD, I sent it to &lt;a href=&quot;https://pcbway.com/g/245M3j&quot;&gt;PCBWay&lt;/a&gt; to be manufactured. PCBWay provides a &lt;a href=&quot;https://github.com/PCBWay/PCBWay-Plug-in-for-Kicad&quot;&gt;KiCAD plugin&lt;/a&gt; which automates the process of sending your board layout from KiCAD into their service. Gone are the days of fussing around with Gerber file export settings or realizing you forgot to attach a drill file! At time of writing, part of the cost of your order will be donated to the KiCAD project to support future development.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3477&quot; height=&quot;1956&quot; src=&quot;https://breq.dev/_astro/bare-pcb.DkOli565_Z7Yb7p.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The boards for this project arrived quickly andy well-packaged with no defects and high-quality silkscreen printing. While I chose a standard thickness and silkscreen color for this project, PCBWay offers many more customization options often at minimal additional cost.&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/silkscreen-title.BW7-aghd_1fTfqu.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/silkscreen-ilda.Djo-GVDz_ZUiQwc.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Of course, I took the opportunity to put some fun graphics and text on the board, including a reproduction of the famous ILDA test pattern. Everything looks impressively sharp and consistent across all of the boards I received. I wrote our names in text only 0.75mm tall, and despite KiCAD DRC warning me about the letter height, it came out looking perfectly legible.&lt;/p&gt;
&lt;p&gt;The full schematic is in the GitHub repo, but I’ll highlight some of the most interesting parts:&lt;/p&gt;
&lt;h2 id=&quot;power-regulation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#power-regulation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Power Regulation&lt;/h2&gt;
&lt;p&gt;This project needs a few different voltage rails to function:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;+12V for the laser diodes&lt;/li&gt;
&lt;li&gt;±15V for the galvo driver board&lt;/li&gt;
&lt;li&gt;+3.3V for the ESP32&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I managed to get away with not having a 5V rail for this project which was nice, and the galvos I bought came with a power supply to step down 120/240V AC to the ±15V rails. I decided to have line voltage enter the system through an IEC connector on the PCB so that it can be routed internally to both this off-board ±15V supply and regulated down to 12V on the board. This was my first time routing AC voltage on a board, and I’m sure the trace spacing, etc. is far higher than it needs to be, but it seems to work great! The one important thing I learned was to avoid having the ground plane get too close to the AC traces to reduce interference. That, and the habit of grabbing a board by the connectors while it’s on is not advisable when said connectors have 120V present.&lt;/p&gt;
&lt;p&gt;To step down to +12V, I wanted an on-board solution to reduce the number of components in the project. We made use of an &lt;a href=&quot;https://www.digikey.com/en/products/detail/mean-well-usa-inc/IRM-05-12/7704648&quot;&gt;Mean Well IRM series module&lt;/a&gt;, which has a simple 4-pin footprint and completely solved the 12V rail problem. While it takes up some space on the board, it’s still more compact than most off-board options would be and requires no additional wiring.&lt;/p&gt;
&lt;p&gt;Finally, we needed to step 12V down to 3.3V. Most examples of this in schematics I found online use the AMS1117 linear regulator or its clones, but I also found countless Reddit comments arguing that the AMS1117 is a bad choice because it’s inefficient compared to modern switching regulators. I ended up using &lt;a href=&quot;https://webench.ti.com/power-designer/&quot;&gt;TI Webench Power Designer&lt;/a&gt; to come up with a design and directly copied the schematic into my board. The design uses the &lt;a href=&quot;https://www.ti.com/lit/ds/symlink/tps561201.pdf&quot;&gt;TPS561201&lt;/a&gt; which is a switching regulator large enough to hand-solder that doesn’t require many external components and has reasonably good efficiency.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;402&quot; height=&quot;207&quot; src=&quot;https://breq.dev/_astro/BuckConverter.DRXsu2s8_Z1SJsnW.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;One thing to note about switching regulators is that the layout of passive components around them on the PCB is important to improve output quality. I was able to copy the example given in the chip datasheet and achieve good results.&lt;/p&gt;
&lt;h2 id=&quot;microcontroller&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#microcontroller&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Microcontroller&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2122&quot; height=&quot;523&quot; src=&quot;https://breq.dev/_astro/Microcontroller.DvZAdJyn_Z2hjXGc.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The project is built around an ESP32 module, since I did not want to have to design my own antenna around the chip and we had enough space to spare.&lt;/p&gt;
&lt;p&gt;The ESP32 has a native USB port which I wanted to potentially use to control the device from a computer. While it is possible to upload code over the native USB port, the development experience is far from optimal for two reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Logging and panic handlers in Rust only output to the chip’s UART pins&lt;/li&gt;
&lt;li&gt;Uploading code requires performing a particular sequence to the BOOT and RESET pins which can’t be done over the native USB&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thus, I tried to copy the schematic found in most first-party ESP32 devboards: adding a USB to UART chip and using two transistors to implement BOOT and RESET pins via the serial DTR and RTS signals.&lt;/p&gt;
&lt;p&gt;Based on my previous experience hand-soldering a surface mount USB-C connector for my recent &lt;a href=&quot;https://breq.dev/projects/matrix3&quot;&gt;LED matrix sign&lt;/a&gt; project, I decided to try using USB-C connectors for both the native and debug USB ports on this board, too. What I failed to consider was that the 6-pin power-only connectors on the LED matrix had a tight-but-workable 1.2mm pitch, but the 16-pin connector I had to use to carry data signals had a tiny 0.5mm pitch. My skills and equipment were not up to the task.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;500&quot; height=&quot;500&quot; src=&quot;https://breq.dev/_astro/usbc-pinout.CFumpcvA_Z19bu5m.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;caption&quot;&gt;
&lt;p&gt;Soldering a power-only USB-C connector is far easier than one supporting USB 2.0. Based on a diagram on &lt;a href=&quot;https://en.wikipedia.org/wiki/File:USB_Type-C_Receptacle_Pinout.svg&quot;&gt;Wikimedia Commons&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;I looked into getting an FTDI chip but it seems like there’s a substantial shortage right now — the FT232RNL was out of stock on DigiKey at the time I designed the board, and is still out of stock months later at the time of writing. Most boards I found used the CP2102N chip instead. It only comes in a tiny 24-QFN (“quad flat no-lead”) package with 0.5mm spacing between pins. I figured it would be easy enough to hand-solder, but missed the fact that the only ground pin on the chip is located on the underside.&lt;/p&gt;
&lt;p&gt;I ended up using a heat gun and lots of flux to get the chip into place, then using my soldering iron and a drag technique for the pins around the outside. It eventually did seem to work, but I might have burnt a small spot on the dining room table in the process…&lt;/p&gt;
&lt;p&gt;When designing, I added a header to manually hook up UART signals and physical buttons for the BOOT and RESET signals as a backup since I knew this would be the hardest part of the board to assemble, and I’m thankful I did!&lt;/p&gt;
&lt;h2 id=&quot;galvo-signal-handling&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#galvo-signal-handling&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Galvo Signal Handling&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1270&quot; height=&quot;476&quot; src=&quot;https://breq.dev/_astro/Galvo.D7fq95Ot_3NKC9.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The galvos take a pair of analog signals (one for X and one for Y) as their input. The board we made supports three possible sources for these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The ESP32’s two DAC output pins (8-bit)&lt;/li&gt;
&lt;li&gt;An external SPI-based &lt;a href=&quot;https://ww1.microchip.com/downloads/en/devicedoc/22250a.pdf&quot;&gt;MCP4922&lt;/a&gt; DAC (12-bit)&lt;/li&gt;
&lt;li&gt;A 3.5mm audio input source, so you can (for instance) play oscilloscope music from a laptop&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I included a footprint for the external DAC in case it seemed like we were overly limited by the resolution of the ESP32 DAC, but so far that hasn’t been the case yet! The “smoothing” effect created by the inertia of each galvo definitely helps reduce the stair-stepping that would otherwise occur with 8-bit output. The simulator looks super blocky, but in real life everything looks nice and smooth.&lt;/p&gt;
&lt;p&gt;Selection between the active DAC and the external audio input is handled using the presence detection pins of the 3.5mm input jack. We found a part that has two separate pins for detection, allowing us to easily swap in the DAC signal when no cable is present without any other components.&lt;/p&gt;
&lt;p&gt;The harder part is amplifying the galvo signal. The galvo board takes in a differential signal for each axis, where the relative voltage between the two input wires is measured to reduce electromagnetic interference. We needed to take a 0-3.3V input and convert it to a differential signal of ±10V.&lt;/p&gt;
&lt;p&gt;Op-amps confuse me, so I made Mia do all the hard work of this part! The tricky part of the op-amp circuit is that we need to first scale the DAC output signals to be centered on 0V (removing the 1.65V DC offset), then scale everything up to ±10V.&lt;/p&gt;
&lt;p&gt;I found several designs online that use three op-amps per channel — one to remove the offset, one to scale the input to 0-5V, and another to flip the input and scale it to 0 to -5V, thus generating the required -10V to +10V differential signal. We chose to take a shortcut by generating a single signal ranging from -10V to +10V and hooking the other end of the differential input up to ground, allowing us to use only two op-amps per channel and thus a single &lt;a href=&quot;https://www.ti.com/lit/ds/symlink/tl084.pdf&quot;&gt;TL084&lt;/a&gt; chip. While the first approach is more compliant with the ILDA standard, both approaches do produce a valid signal where the voltage difference between the two output pins ranges from -10V to +10V.&lt;/p&gt;
&lt;p&gt;One concern we had was that it was possible to drive the input lines beyond the intended range, potentially causing damage to the galvos or driver board. The op-amps we used have a rail voltage of ±15V, so without any additional limiting, signals that are too big in scale could cause the galvos to deflect too far out of range. To guard against this, we added 10V Zener diodes at the output which will clip the output voltage to within the allowable range.&lt;/p&gt;
&lt;p&gt;After some prototyping in &lt;a href=&quot;https://www.falstad.com/circuit/&quot;&gt;Falstad Circuit Simulator&lt;/a&gt;, we went straight to PCB design — it was definitely a risk to skip the breadboard prototyping stage, but everything worked on the first try!&lt;/p&gt;
&lt;h2 id=&quot;laser-control&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#laser-control&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Laser Control&lt;/h2&gt;
&lt;p&gt;Laser diodes, like LEDs, are best driven using a constant-current power supply. The laser driver board that came with the diodes I bought uses an &lt;a href=&quot;https://datasheet4u.com/pdf-down/O/C/5/OC5211-OCX.pdf&quot;&gt;OC5211&lt;/a&gt; driver for each channel. Each channel provides a digital input signal that allows for dimming of the laser using PWM. While the datasheet specifies that the input pin needs to be a 5V signal (and doesn’t provide a range of acceptable values), I found that I can drive it from a 3.3V GPIO pin without any difficulties.&lt;/p&gt;
&lt;p&gt;Unfortunately, this design presents a major safety issue: if the digital input signal is in a high-impedance state, the OC5211 has an internal pull-up to 5V, so the laser will be set to full brightness. While I had hoped this was a pull-up resistor on the board I could manually remove, it seems like this pull-up resistor is actually contained within the chip itself?&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1169&quot; height=&quot;816&quot; src=&quot;https://breq.dev/_astro/OC5211.DRyc8h9c_1gOUzW.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;This diagram seems to imply that the chip contains an onboard 5V regulator solely for the purpose of the built-in pull-up resistor?&lt;/p&gt;
&lt;p&gt;As a workaround, I decided to add switching to the 12V power supply going to the laser driver. The laser driver board requires high-side switching with a P-channel MOSFET, since the ground pin of the power input is shared with the reference ground for the modulation signals. However, as I can’t output +12V from a GPIO, I added a pull-up resistor from the gate to +12V and an NPN transistor such that the GPIO can pull the gate low.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;655&quot; height=&quot;302&quot; src=&quot;https://breq.dev/_astro/Laser.C12I4wn6_4lYx9.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;While this is extremely far from a professional product, I wanted to try out some of the steps that would be required for this type of design. Most lasers of this output power have an E-stop interlock and/or keyswitch that must be turned before the laser can be operated, so I replicated this design here. I also added a voltage divider such that the status of the interlock can be read by the ESP32.&lt;/p&gt;
&lt;h2 id=&quot;i2c-and-wii-accessories&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#i2c-and-wii-accessories&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;I2C and Wii Accessories&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1883&quot; height=&quot;1883&quot; src=&quot;https://breq.dev/_astro/kicad_footprint.C4bTqo1v_55uRS.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I knew early on that I wanted to use Wii Nunchucks as a controller for this project, as they’re &lt;a href=&quot;https://breq.dev/2026/03/15/wii-accessories&quot;&gt;uniquely well-suited for homemade projects&lt;/a&gt;. All Nunchucks share the same I2C address, so I had to put the two ports on the two different I2C peripherals. I wanted to add additional room for extensibility to the project (such as for a small indicator display?), but since anything else would likely have a different I2C address to the Nunchuck, I chained those connectors off of the same I2C buses as the Nunchuck inputs. I used &lt;a href=&quot;https://www.sparkfun.com/qwiic&quot;&gt;Qwiic&lt;/a&gt; connectors for easy wiring — they’re quite compact but still possible to hand-solder.&lt;/p&gt;
&lt;h1 id=&quot;enclosure-and-construction&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#enclosure-and-construction&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Enclosure and Construction&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1310&quot; height=&quot;1126&quot; src=&quot;https://breq.dev/_astro/enclosure-cad.BNJZVEPo_qscdD.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I chose to 3D print the enclosure for this project. At slightly over 200 by 200 mm, it’s definitely on the larger end of what I’m capable of producing on a consumer-grade FDM printer, and failed prototypes were a little more expensive than I was hoping with each print costing about $5 in filament. In the past, I’ve used &lt;a href=&quot;https://breq.dev/projects/itx-case&quot;&gt;wood and FDM parts together&lt;/a&gt; to make cases of this size, but my apartment lacks woodworking tools and I didn’t want to have to drive to my parents’ garage to build this thing.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/printer.vZUcojz4_ZbhoCc.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Overall it came out pretty well! The bridging areas I worried about came out alright, and the tolerances for screw holes and the like were perfect. The one issue I encountered was warping — the print looked great on the build plate but warped significantly once removed. This was more of an issue for the top plate than the baseplate and walls, but things look relatively alright once screwed together.&lt;/p&gt;
&lt;p&gt;I tried to mount individual components on their own adapters to give as much flexibility for later alignment as I might need — if the laser beam misses the mirror by a millimeter, I didn’t want to reprint the entire enclosure. I put the RGB laser module on slots to make later adjustment easier.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2978&quot; height=&quot;2979&quot; src=&quot;https://breq.dev/_astro/internal.BkowJ5Um_Z1aKopY.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I added a small 12V fan, as the galvo driver board gets quite hot during operation.&lt;/p&gt;
&lt;p&gt;Wire management is definitely lackluster, but airflow still seems fine and there are no moving parts to worry about except the mirrors. Many of the cables in use could definitely be re-crimped to a shorter length.&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/io-shield-angle.DKlSu3NY_1hSjT8.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/io-plugged.D0wZx0Q6_Z1dTWPm.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/fan-power.CGtjVmTv_Z1hLA4I.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/front-view.CrtH6Y9S_1CpAzR.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;I was dreading making the I/O shield, but it ended up being not too tricky! For each revision, I printed just a single layer before taking it off the printer and test-fitting it to confirm the fit. Once I found one that worked, I printed it at 4mm thickness since it’s structurally used to hold the lid in place.&lt;/p&gt;
&lt;h1 id=&quot;firmware&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#firmware&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Firmware&lt;/h1&gt;
&lt;p&gt;The ESP32 is notable for its highly developed Rust support. I am a big fan of the Rust language, but haven’t had a chance to use it for firmware development yet. Honestly, I was surprised by how easy it was for me to get started! The ESP32 is the only chip for which the hardware abstraction layers are maintained by the vendor itself (Espressif) instead of by the community.&lt;/p&gt;
&lt;p&gt;While Rust ships a large standard library by default, most of its functions rely on having a memory allocator and various common operating system functions like threading, a system clock, synchronization primitives like mutexes, and more. Code running on a microcontroller doesn’t have access to these! Thankfully, most of the standard library functions have direct equivalents: the &lt;code&gt;core&lt;/code&gt; crate contains things like the standard builtin types, and &lt;code&gt;alloc&lt;/code&gt; crate contains strings, vecs, treemaps, and other collections. I did have to pull in the &lt;a href=&quot;https://crates.io/crates/libm&quot;&gt;&lt;code&gt;libm&lt;/code&gt;&lt;/a&gt; crate in for floating-point math functions, but there’s a &lt;a href=&quot;https://github.com/rust-lang/rust/issues/137578&quot;&gt;tracking issue&lt;/a&gt; to get those merged into &lt;code&gt;core&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For an RTOS, I’m using &lt;a href=&quot;https://embassy.dev/&quot;&gt;Embassy&lt;/a&gt;, which gives async/await support. I am not used to programming in an environment where I have full async/await, but lack most of the standard library! That said, the async approach seems to be a very good fit for embedded code, especially with things like networking involved.&lt;/p&gt;
&lt;p&gt;A large part of the fun of this project was just seeing how many sources of vector data I could display with the projector!&lt;/p&gt;
&lt;h2 id=&quot;ilda-test-pattern&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#ilda-test-pattern&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;ILDA Test Pattern&lt;/h2&gt;
&lt;p&gt;Of course, it makes sense to start by verifying our system is tuned properly. There are a lot of adjustment knobs on the galvo driver board!&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-5xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1722&quot; height=&quot;1722&quot; src=&quot;https://breq.dev/_astro/ilda-large.CYhgJ0iZ_Z1R3qDT.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;866&quot; height=&quot;970&quot; src=&quot;https://breq.dev/_astro/ilda-pattern-diagram.CO_Nz6nc_2r3gOC.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;caption&quot;&gt;
&lt;p&gt;Source: &lt;a href=&quot;https://www.ilda.com/resources/StandardsDocs/ILDA_TestPattern95_rev002.pdf&quot;&gt;ILDA Test Pattern (1995)&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The International Laser Display Association (ILDA) provides a test pattern that can be used for the tuning of RGB galvo-based projectors, alongside a procedure to adjust the PID constants of the galvo driver for the most accurate shape.&lt;/p&gt;
&lt;p&gt;One difference between the ILDA pattern and my setup is that the pattern expects the use of a third galvo scanner to act as a “blanking” control, providing analog dimming of the laser light. Nowadays, most lasers and controllers support high-speed analog modulation of the laser light directly, but historically it was more common to use a physical beam dump attached to a galvo that could move in and out of the light path. The modulation control I have over the laser is quite imperfect, which is probably the reason that the blanking pattern looks the worst out of all parts of the pattern.&lt;/p&gt;
&lt;p&gt;A properly-tuned ILDA projector should have the blue circle inscribed into the green square — although the points making up the blue circle are spaced out larger than the square, the intended effect is that the galvos undershoot the desired path and create an inscribed circle. In my case, the galvos are “too good” and thus trace something closer to the true path. However, if I scale up the image using the potentiometer on the op-amps, the image becomes closer to the result that ILDA intended. Since I don’t really care about interfacing with the rest of the laser ecosystem, I deliberately tuned it like this so that graphics would look truer to shape regardless of scaling (up to a limit).&lt;/p&gt;
&lt;p&gt;You can also see some “pincushion distortion”: the left and right edges of the outer rectangle look almost “pinched in”. This is an unavoidable artifact of the geometry of an X/Y galvo setup like this. This is often compensated for either in software or with an analog circuit to adjust the X and Y input signals.&lt;/p&gt;
&lt;h2 id=&quot;text-and-retro-fonts&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#text-and-retro-fonts&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Text and Retro Fonts&lt;/h2&gt;
&lt;p&gt;Vector displays excel at displaying crisp vector-based fonts. While the stroke width is unfortunately fixed, shapes look sharp even when viewed from close up.&lt;/p&gt;
&lt;p&gt;When I first started playing around with the galvo, I found few libraries for rendering these vector fonts, and even fewer written in Rust. So &lt;a href=&quot;https://breq.dev/projects/vector-text&quot;&gt;I wrote my own&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;A few of my favorites:&lt;/p&gt;
&lt;p&gt;The classic Hershey fonts look excellent. The simplex styles are fast enough to draw such that they’re visible in person, and the more complicated styles still look great in long-exposure photos.&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;834&quot; height=&quot;834&quot; src=&quot;https://breq.dev/_astro/hershey_simplex.BF1V54WL_ZGV8wO.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1025&quot; height=&quot;1024&quot; src=&quot;https://breq.dev/_astro/hershey_gotheng.8KGT-C66_20SJ7Q.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;KiCAD’s NewStroke shares similarities with the Hershey fonts, and looks great in person as well.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1039&quot; height=&quot;1040&quot; src=&quot;https://breq.dev/_astro/newstroke.CkvcKJ2a_Z1FKHPA.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The Borland Graphics Interface fonts bring much more dramatic styling to the mix. You might recognize LITT.CHR from CadSoft EAGLE (rest in peace!)&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1142&quot; height=&quot;1142&quot; src=&quot;https://breq.dev/_astro/borland_litt.Dtih_jNc_2saraN.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1197&quot; height=&quot;1196&quot; src=&quot;https://breq.dev/_astro/borland_euro._AuBDx0O_1Y8etj.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;asteroids&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#asteroids&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Asteroids&lt;/h2&gt;
&lt;p&gt;Of course, I wanted to game on this thing. I made an extremely barebones implementation of Asteroids (after all, I was more interested in the display than the gameplay). It looks awesome!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1266&quot; height=&quot;1266&quot; src=&quot;https://breq.dev/_astro/asteroids.BeorKfc1_1Od7qF.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;This example was a bit hard to photograph, since the game framerate varied based on the number of objects on screen so I couldn’t quite dial in my camera shutter speed right. As a result, you can see multiple frames in this image with slightly different object positions.&lt;/p&gt;
&lt;h2 id=&quot;arbitrary-svg-data&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#arbitrary-svg-data&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Arbitrary SVG Data&lt;/h2&gt;
&lt;p&gt;One major source of vector graphics in our modern world is SVGs. For this, I decided to preprocess the SVG file on my computer, then build the list of points into the program.&lt;/p&gt;
&lt;p&gt;I wrote a short script using the &lt;a href=&quot;https://github.com/mathandy/svgpathtools&quot;&gt;svgpathtools&lt;/a&gt; library to output a list of points:&lt;/p&gt;
&lt;pre class=&quot;language-py&quot; data-language=&quot;py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; svgpathtools &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; svg2paths&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; wsvg

paths&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; attributes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; svg2paths&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;breqcube.svg&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

points &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; path &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; paths&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    first_point &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; line &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; i &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;line&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            point &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;point&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;line&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            points&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;append&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;point&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;real&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; point&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imag&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; first_point&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            first_point &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;False&lt;/span&gt;

    last &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;point&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    points&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;append&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;last&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;real&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; last&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imag&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pen&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; points&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;+&apos;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; pen &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;-&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It could definitely be improved (notably, interpolating both lines and curves to a hardcoded 10 points is not ideal), but it works well enough with the files I have!&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3010&quot; height=&quot;3010&quot; src=&quot;https://breq.dev/_astro/breqcube.XzX8_Ci9_Yk55K.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2844&quot; height=&quot;2844&quot; src=&quot;https://breq.dev/_astro/bunny.yzxGLc1Y_Z28BYDS.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;maps-via-openstreetmap&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#maps-via-openstreetmap&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Maps via OpenStreetMap&lt;/h2&gt;
&lt;p&gt;Another source of vector information is maps. OpenStreetMap provides a nice interface to download map data for vector display.&lt;/p&gt;
&lt;p&gt;The most powerful way to extract data from OpenStreetMap is through the Overpass API. To formulate a request, I started using the &lt;a href=&quot;https://overpass-turbo.eu/&quot;&gt;Overpass Turbo&lt;/a&gt; interface to tune my query to return the types of features I wanted — I wanted to show enough recognizable neighborhood features without too many driveways, parking lots, or sidewalks causing the galvos to spend excessive time drawing the image.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2796&quot; height=&quot;1855&quot; src=&quot;https://breq.dev/_astro/overpass-turbo.OyZz0Gmz_Z16pwyJ.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once I had the query nailed down, I could just paste the payload into a Python script that could take the latitude, longitude, and map size as arguments. Making a query programmatically is as simple as &lt;code&gt;POST&lt;/code&gt;-ing it to &lt;code&gt;https://overpass-api.de/api/interpreter&lt;/code&gt; and receiving back some JSON:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot; data-language=&quot;json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;generator&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Overpass API 0.7.62.10 2d4cfc48&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;elements&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;way&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8604814&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;geometry&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;lat&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42.3955979&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;lon&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;-71.12024&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;lat&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42.3956344&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;lon&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;-71.1203257&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;lat&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42.3956969&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;lon&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;-71.1204748&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;tags&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;highway&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;secondary&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Highland Avenue&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;oneway&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yes&quot;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From here, it’s easy enough to plot the output and even color the lines differently based on the road type!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1942&quot; height=&quot;1942&quot; src=&quot;https://breq.dev/_astro/davis.Bi5z84BZ_Yp6S7.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;oscilloscope-music&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#oscilloscope-music&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Oscilloscope Music&lt;/h2&gt;
&lt;p&gt;Oscilloscope music is an art form in which people create audio files which contain both audible music and display images when connected to an XY-mode oscilloscope (where the left audio channel drives the X axis and the right audio channel drives the Y axis). Of course, any music would create some interesting patterns on an oscilloscope, but oscilloscope music is specifically designed to create recognizable images or intricate patterns.&lt;/p&gt;
&lt;p&gt;Since the laser projector has a 3.5mm input which overrides the built-in DAC, playing oscilloscope music doesn’t really require its own firmware (other than “keep the laser on at full blast”), but it’s such a fun application that I can’t help but mention it.&lt;/p&gt;
&lt;div class=&quot;max-w-2xl mx-auto&quot;&gt;
&lt;youtube id=&quot;yUsuyAE-pGs&quot; square=&quot;&quot;&gt;
&lt;/youtube&gt;&lt;/div&gt;
&lt;p&gt;The above is &lt;a href=&quot;https://www.youtube.com/watch?v=J2YQD8Go_Hc&amp;#x26;list=PLc4EnsriUcfQPomSF3Eh6sB143HE2r0tf&quot;&gt;Globetrotter by Chris Allen&lt;/a&gt;. While I’ve overdubbed the audio in the recording, the galvos themselves actually do work as a speaker since the physical mirror moving vibrates the air.&lt;/p&gt;
&lt;h1 id=&quot;lessons-learned&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#lessons-learned&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Lessons learned&lt;/h1&gt;
&lt;h2 id=&quot;surface-mount-soldering&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#surface-mount-soldering&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Surface mount soldering&lt;/h2&gt;
&lt;p&gt;When I first attempted surface mount soldering for the &lt;a href=&quot;https://breq.dev/projects/matrix3&quot;&gt;LED matrix project&lt;/a&gt; I made late last year, I found working with large 0805 and SOIC parts surprisingly straightforward with the soldering iron and tools I already had on hand. It is with that confidence that I decided to attempt to solder chips with even tighter spacing — if going from DIP to SOIC was that easy, was QFN all that much of a leap?&lt;/p&gt;
&lt;p&gt;As it turns out, yes it is! While I enjoy learning new skills by doing things and trying stuff out, I think I will have to admit defeat for now on some of the more advanced parts I was hoping to solder.&lt;/p&gt;
&lt;p&gt;It wasn’t just one tool I was missing out on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Checking the joint quality or looking for bridging on 0.5mm pitch components was quite difficult without a microscope (and my phone camera’s macro mode was a rather inefficient workaround)&lt;/li&gt;
&lt;li&gt;Accurately controlling the amount of solder being deposited on pads was difficult when working with a spool of 0.8mm diameter wire, where something smaller would give me much better control&lt;/li&gt;
&lt;li&gt;While my fine-point conical soldering iron tip worked great, a fine chisel would have given me more control over keeping solder on the tip&lt;/li&gt;
&lt;li&gt;Isopropyl alcohol and a toothbrush would help clean up the mess left behind by flux&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I do really want to go back and practice more fine-pitched surface-mount soldering. Going from DIP to SOIC gave me access to so many more parts and so much more flexibility with project designs, and I find more advanced soldering work to be quite fun and rewarding! I hope to eventually get more confident with even smaller package sizes.&lt;/p&gt;
&lt;h2 id=&quot;unforgiving-programming&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#unforgiving-programming&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;“Unforgiving” programming&lt;/h2&gt;
&lt;p&gt;I often enjoy working on projects where the line between “awesome demo” and “totally unimpressive” can be a very small difference in code optimization. My &lt;a href=&quot;https://breq.dev/2023/06/17/ldsp&quot;&gt;past work with audio processing&lt;/a&gt; is a good example of that — if you miss filling an audio buffer in time, you get glitching and stuttering that makes your project unusable. Contrast this with most of software development, where processing power is abundant and optimizations are simply a nice-to-have.&lt;/p&gt;
&lt;p&gt;This project certainly involved lots of tuning. Running the galvos too slowly produces flickering outputs, but running them too fast makes the output lose crispness as corners get rounded off. There is tons of room for optimization: points where the laser turns on or off need more dwell time than points where the color is constant, points in a straight line can be output faster than points with sharp corners, and optimizing the path that the beam takes to trace out a shape to avoid sharp corners and on/off switches can yield substantial performance gains.&lt;/p&gt;
&lt;h2 id=&quot;mechanical-design&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#mechanical-design&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Mechanical design&lt;/h2&gt;
&lt;p&gt;I’ve mostly viewed CAD software as a means to an end — I wanted a part with a certain shape, so I would put up with the process of modeling so I could get it on my 3D printer. But with this project, I truly had a ton of fun with designing everything! Having such a blank slate to work off of, exploring tradeoffs with component positioning, and eventually settling on a final outer form was so rewarding.&lt;/p&gt;
&lt;p&gt;I think I want to learn a better CAD tool — I learned SketchUp back in middle school, but the free tier has been slowly decreasing in usefulness over time, and I’m getting more frustrated with its inability to represent curves nicely as I move towards more complex designs.&lt;/p&gt;
&lt;h1 id=&quot;bibliography&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#bibliography&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Bibliography&lt;/h1&gt;
&lt;p&gt;Early in this project, I decided to look for writeups of similar projects people have attempted. I found a surprisingly wide variety of projects, and definitely took inspiration from many of them when designing my projector!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;https://stanleyprojects.com/projects/ildawavex16&quot;&gt;ILDAWaveX16&lt;/a&gt; by StanleyProjects is an open-source device for controlling ILDA-compliant laser projectors. While it also uses an ESP32, it takes a much more serious approach to clean signal generation, including using a dedicated DAC chip for 16-bit resolution (compared to the 8-bit resolution of my setup).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=bdo3djJrw9o&quot;&gt;Bitluni’s laser projector setup&lt;/a&gt; uses a phosphor to simulate a CRT-style screen, and adapts a smartphone telephoto lens to increase the deflection of the galvo output without pushing the galvos to higher angles and burning them up.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/atomic14/esp-asteroids&quot;&gt;Atomic14’s projector&lt;/a&gt; has only a single color but seems to be the first example I can find of Asteroids running on a laser galvo setup. I found the discussion on the difficulty of rendering Hershey fonts very informative.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=fEPicBSYeNQ&quot;&gt;BenMakesEverything’s raster projector&lt;/a&gt; is an interesting take on this approach which uses a polygon scanner for the X-axis and a galvo for the Y-axis to draw scan lines in a similar manner to a CRT. It excels at displaying existing raster-based content such as animated GIFs.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.limpkin.fr/index.php?post/2008/11/03/XY-Laser-Scanner&quot;&gt;limpkin’s XY scanner&lt;/a&gt; was made back in 2007, but shares a lot of similarities with recent projects. One interesting difference is that it makes use of a hardware blanking galvo (ripped out of an old hard drive), as opposed to modern solutions which modulate the current through the laser diode directly.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Playing with Wii Extension Controllers</title><link>https://breq.dev/2026/03/15/wii-accessories/</link><guid isPermaLink="true">https://breq.dev/2026/03/15/wii-accessories/</guid><description>I2C-based game controllers on the cheap!</description><pubDate>Sun, 15 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently spent some time writing a driver for Wii accessories (like the Nunchuck and Classic Controller). I’ve published it on crates.io as &lt;a href=&quot;https://crates.io/crates/wii-accessories&quot;&gt;&lt;code&gt;wii-accessories&lt;/code&gt;&lt;/a&gt;. Here’s a deep dive into why I chose to do this and how these controllers work under the hood!&lt;/p&gt;
&lt;h2 id=&quot;the-search-for-a-joystick&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-search-for-a-joystick&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The search for a joystick&lt;/h2&gt;
&lt;p&gt;Recently, I was working on a project and looking for an easy way to add joystick controllers. Broadly, solutions to this problem fall under three categories.&lt;/p&gt;
&lt;p&gt;The first category is USB-based controllers. While more microcontrollers are starting to support USB host mode, the setup required for these is still quite difficult, and the controllers themselves can be relatively expensive. These could be a good fit for projects with a full Linux system, but my project only had a microcontroller.&lt;/p&gt;
&lt;p&gt;The second category is purpose-built controllers designed to be hooked up to microcontrollers, like the &lt;a href=&quot;https://www.sparkfun.com/sparkfun-qwiic-joystick.html&quot;&gt;Sparkfun Quiic Joystick&lt;/a&gt; or &lt;a href=&quot;https://www.adafruit.com/product/5743&quot;&gt;Adafruit STEMMA Gamepad&lt;/a&gt;. These have good library support, but aren’t as ergonomic as an actual consumer controller and don’t come with an enclosure. They certainly fill a niche, but aren’t what I’m looking for.&lt;/p&gt;
&lt;p&gt;This brings us to the third category: controllers built for older consoles. Unfortunately, most of these are hard to use in practice. The NES and SNES, for instance, used a shift-register API that’s easy for any modern microcontroller to implement, but connectors for them are hard to come by and they lack an analog stick. The PlayStation used a protocol that’s &lt;a href=&quot;https://hackaday.io/project/170365-blueretro/log/186471-playstation-playstation-2-spi-interface&quot;&gt;&lt;em&gt;almost&lt;/em&gt; SPI&lt;/a&gt; but with a non-standard additional pin for data acknowledgement. The N64 and GameCube used a bespoke half-duplex serial protocol which is &lt;a href=&quot;https://www.qwertymodo.com/hardware-projects/n64/n64-controller&quot;&gt;tricky to implement with standard peripherals&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All is not lost, though. As it turns out, the Wii Nunchuck manages to be almost perfect for this use case:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Standard I2C interface.&lt;/strong&gt; The Nunchuck connector has just 5 pins: VCC, GND, SDA, SCL, and a presence detection pin. It can even happily share a bus with other I2C devices.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3.3V Power.&lt;/strong&gt; In an era where the vast majority of microcontrollers have moved to 3.3V power and logic, this eliminates the need for a separate 5V supply in many projects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;(Sorta) easy connectors.&lt;/strong&gt; While buying an actual expansion card connector is tricky, the Nunchuck connector can be easily emulated with an edge connector on a PCB.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cheap and easy to find.&lt;/strong&gt; You might already have one around, but if not, third-party versions of the controller are easy to find and purchase online.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These benefits also apply to other controllers which use this port such as the Wii Classic Controller, giving even more options for interfacing.&lt;/p&gt;
&lt;h2 id=&quot;pcb-connector&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#pcb-connector&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;PCB Connector&lt;/h2&gt;
&lt;p&gt;Since both the connector pins and locking tabs lie in the same plane, a PCB edge connector can create a solid and reliable connection! Several breakout boards have been designed around this principle and are available for cheap online.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1453&quot; height=&quot;1094&quot; src=&quot;https://breq.dev/_astro/nunchucky.Bm8InaUY_Z24EJ4H.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;caption&quot;&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.solarbotics.com/product/31040/&quot;&gt;Nunchucky&lt;/a&gt;, the earliest example I could find of this edge connector technique.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Building this connector into a custom board is also quite straightforward. There’s a KiCAD footprint available in this &lt;a href=&quot;https://gitlab.com/kicad/libraries/kicad-footprints/-/merge_requests/3351&quot;&gt;merge request&lt;/a&gt; that has both the pad spacing and the required board outline cuts to create the connector shape.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1883&quot; height=&quot;1883&quot; src=&quot;https://breq.dev/_astro/kicad_footprint.C4bTqo1v_55uRS.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Although my girlfriend remarked that “getting a footprint from an open merge request to the KiCAD repo is like copying code from the question part on StackOverflow,” the connector seems to work great on my boards! The fit is best with a 2.0mm thick board, but I went with the standard 1.6mm thickness and found it to still work well.&lt;/p&gt;
&lt;h2 id=&quot;implementing-a-rust-driver&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#implementing-a-rust-driver&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Implementing a Rust driver&lt;/h2&gt;
&lt;p&gt;While the I2C protocol is not officially documented anywhere, the Wii homebrew community has made excellent progress in reverse-engineering it. Let’s walk through some code examples in Rust to understand the protocol.&lt;/p&gt;
&lt;p&gt;All Wii accessories use an I2C address of &lt;code&gt;0x52&lt;/code&gt;. You might assume that the Wii Motion Plus uses a different address from other controllers since it allows daisy-chaining, but Nintendo instead opted to invent their own complex “passthrough” modes of passing through data for reasons unknown.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; data-language=&quot;rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;WII_ACCESSORY_ADDR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x52&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nintendo decided to obfuscate data going over the I2C connection, likely to make it more difficult to reverse-engineer. With this enabled, lookup tables need to be used to de-obfuscate the data. Thankfully, this can be disabled by writing value &lt;code&gt;0x55&lt;/code&gt; to register &lt;code&gt;0xF0&lt;/code&gt; and then writing &lt;code&gt;0x00&lt;/code&gt; to register &lt;code&gt;0xFB&lt;/code&gt;, regardless of the connected controller type.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; data-language=&quot;rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;i2c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;WII_ACCESSORY_ADDR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0xF0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x55&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xFB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The six bytes starting at &lt;code&gt;0xFA&lt;/code&gt; identify the controller model:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; data-language=&quot;rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;i2c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;WII_ACCESSORY_ADDR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0xFA&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; identifier &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
i2c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;WII_ACCESSORY_ADDR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; identifier&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Identifier&lt;/th&gt;&lt;th&gt;Controller Model&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;0000 A420 0000&lt;/td&gt;&lt;td&gt;Nunchuck&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0000 A420 0101&lt;/td&gt;&lt;td&gt;Classic Controller&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0100 A420 0101&lt;/td&gt;&lt;td&gt;Classic Controller Pro (no analog triggers)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;…&lt;/td&gt;&lt;td&gt;&lt;em&gt;full table on &lt;a href=&quot;http://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way&quot;&gt;wiibrew.org&lt;/a&gt;&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;From here, we can start to read data! Data bytes start at &lt;code&gt;0x00&lt;/code&gt; for all extension controller types. A report from the Nunchuck includes joystick data, accelerometer data, and the “C” and “Z” buttons.&lt;/p&gt;
&lt;table class=&quot;border-collapse font-mono text-sm w-full max-w-xl mx-auto table-fixed&quot;&gt;
  &lt;colgroup&gt;
    &lt;col class=&quot;w-14&quot;&gt;
    &lt;col&gt;&lt;col&gt;&lt;col&gt;&lt;col&gt;&lt;col&gt;&lt;col&gt;&lt;col&gt;&lt;col&gt;
  &lt;/colgroup&gt;
  &lt;thead&gt;
    &lt;tr class=&quot;&quot;&gt;th]:pb-1 [&amp;#x26;&gt;th]:text-center [&amp;#x26;&gt;th]:text-xs [&amp;#x26;&gt;th]:text-gray-400 [&amp;#x26;&gt;th]:font-medium [&amp;#x26;&gt;th:not(:first-child)]:border-b-2 [&amp;#x26;&gt;th:not(:first-child)]:border-gray-700&quot;&gt;
      &lt;th class=&quot;text-left&quot;&gt;&lt;/th&gt;
      &lt;th&gt;7&lt;/th&gt;&lt;th&gt;6&lt;/th&gt;&lt;th&gt;5&lt;/th&gt;&lt;th&gt;4&lt;/th&gt;&lt;th&gt;3&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;1&lt;/th&gt;&lt;th&gt;0&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody class=&quot;&quot;&gt;tr]:border-b [&amp;#x26;&gt;tr]:border-gray-200 dark:[&amp;#x26;&gt;tr]:border-gray-700 [&amp;#x26;&gt;tr:last-child]:border-b-0 [&amp;#x26;&gt;tr&gt;td:first-child]:py-1 [&amp;#x26;&gt;tr&gt;td:first-child]:pr-3 [&amp;#x26;&gt;tr&gt;td:first-child]:text-xs [&amp;#x26;&gt;tr&gt;td:first-child]:text-gray-400 [&amp;#x26;&gt;tr&gt;td:not(:first-child)]:py-1 [&amp;#x26;&gt;tr&gt;td]:text-center [&amp;#x26;&gt;tr&gt;td:not(:first-child)]:bg-gray-50 dark:[&amp;#x26;&gt;tr&gt;td:not(:first-child)]:bg-gray-800&quot;&gt;
    &lt;tr&gt;
      &lt;td&gt;0x00&lt;/td&gt;
      &lt;td colspan=&quot;8&quot; class=&quot;border-l border-gray-200 dark:border-gray-700&quot;&gt;Joystick X&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;0x01&lt;/td&gt;
      &lt;td colspan=&quot;8&quot; class=&quot;border-l border-gray-200 dark:border-gray-700&quot;&gt;Joystick Y&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;0x02&lt;/td&gt;
      &lt;td colspan=&quot;8&quot; class=&quot;border-l border-gray-200 dark:border-gray-700&quot;&gt;Accelerometer X [9:2]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;0x03&lt;/td&gt;
      &lt;td colspan=&quot;8&quot; class=&quot;border-l border-gray-200 dark:border-gray-700&quot;&gt;Accelerometer Y [9:2]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;0x04&lt;/td&gt;
      &lt;td colspan=&quot;8&quot; class=&quot;border-l border-gray-200 dark:border-gray-700&quot;&gt;Accelerometer Z [9:2]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;0x05&lt;/td&gt;
      &lt;td colspan=&quot;2&quot; class=&quot;border-l border-r border-gray-200 dark:border-gray-700&quot;&gt;Accel Z [1:0]&lt;/td&gt;
      &lt;td colspan=&quot;2&quot; class=&quot;border-r border-gray-200 dark:border-gray-700&quot;&gt;Accel Y [1:0]&lt;/td&gt;
      &lt;td colspan=&quot;2&quot; class=&quot;border-r border-gray-200 dark:border-gray-700&quot;&gt;Accel X [1:0]&lt;/td&gt;
      &lt;td class=&quot;border-r border-gray-200 dark:border-gray-700&quot;&gt;C&lt;/td&gt;
      &lt;td&gt;Z&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Implementing this in Rust is quite straightforward, albeit tedious:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; data-language=&quot;rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;i2c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;WII_ACCESSORY_ADDR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;i2c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;WII_ACCESSORY_ADDR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x00&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i16&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x01&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i16&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; accel_x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x02&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x3C;&amp;#x3C;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x05&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0b1100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; accel_y &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x03&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x3C;&amp;#x3C;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x05&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0b110000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; accel_z &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x04&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x3C;&amp;#x3C;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x05&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u16&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0b11000000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; c &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x05&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0b0010&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; z &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x05&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0b0001&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most of the other controller options follow quite naturally — while the data is often arranged into messages in a jumbled fashion, the community has done a good enough job with documentation that writing an implementation is straightforward.&lt;/p&gt;
&lt;h2 id=&quot;go-forth-and-have-fun&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#go-forth-and-have-fun&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Go forth and have fun&lt;/h2&gt;
&lt;p&gt;In just a few lines of code, we’ve transformed an old game console accessory into a capable controller that’s easy to use in your next project. It certainly isn’t as ergonomic or featureful as a modern USB gamepad, but it’s a great option for wiring directly to a microcontroller. It ended up being perfect for an ESP32-based project I’m currently working on (which I’ll share more about in a future post!)&lt;/p&gt;
&lt;p&gt;If you have some Wii accessories lying around and a project that could use them, maybe give &lt;a href=&quot;https://crates.io/crates/wii-accessories&quot;&gt;my Rust crate&lt;/a&gt; a try! If you build something with it, I’d love to hear about it.&lt;/p&gt;</content:encoded></item><item><title>Vector Text Rendering Toolkit</title><link>https://breq.dev/projects/vector-text/</link><guid isPermaLink="true">https://breq.dev/projects/vector-text/</guid><description>A Rust library for rendering text using a variety of common vector fonts.</description><pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;503&quot; height=&quot;385&quot; src=&quot;https://breq.dev/_astro/output_fonts.B8cP8kaN_WTiYr.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I wrote a &lt;a href=&quot;https://crates.io/crates/vector-text&quot;&gt;Rust library&lt;/a&gt; for rendering text to a set of points using commonly available vector fonts.&lt;/p&gt;
&lt;h2 id=&quot;why-vector-fonts&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#why-vector-fonts&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Why vector fonts?&lt;/h2&gt;
&lt;p&gt;Most font definitions in our modern world are based on filled shapes. For both printing and text rendering applications, this makes sense. At the end of the day, 99% of all text written on a computer is going to be rasterized into discrete pixels based on whether each pixel is “inside” or “outside” the shape.&lt;/p&gt;
&lt;p&gt;However, there are still applications out there where drawing filled shapes isn’t desirable. Devices such as X/Y plotters, vector CRT displays, laser cutters, and galvo-based laser projectors can only draw strokes. In many cases, depending on the drawing APIs available, it is simply easier to work with drawing text based on a vector font.&lt;/p&gt;
&lt;h2 id=&quot;hershey-fonts&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#hershey-fonts&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Hershey Fonts&lt;/h2&gt;
&lt;p&gt;One of the rather obvious things that one might want to do with a display is, of course, render text with it. For this, we need a font. Even though traditional fonts are based on vector graphics, they make use of filled shapes of varying width which can’t be replicated on a vector display.&lt;/p&gt;
&lt;p&gt;In 1967, the U.S. Naval Weapons Laboratory published a font designed for use with vector displays named after its author Dr. Hershey. The &lt;a href=&quot;https://en.wikipedia.org/wiki/Hershey_fonts&quot;&gt;Hershey font&lt;/a&gt; contained various styles for Latin, Greek, Cyrillic, and Japanese characters. The font was originally developed for display on early cathode ray tube displays which pointed the electron beam based on X and Y signals instead of forming a constant raster pattern as later CRTs did. You might recognize it as the font used by &lt;a href=&quot;https://docs.opencv.org/3.1.0/d0/de1/group__core.html#ga0f9314ea6e35f99bb23f29567fc16e11&quot;&gt;OpenCV&lt;/a&gt; by default.&lt;/p&gt;
&lt;p&gt;Hershey fonts are relatively easy to get up and running with, but do have a rather strange format. A hershey font file (“.jhf”) contains lines like this:&lt;/p&gt;
&lt;pre class=&quot;language-txt&quot; data-language=&quot;txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;  501  9I[RFJ[ RRFZ[ RMTWT
  502 24G\KFK[ RKFTFWGXHYJYLXNWOTP RKPTPWQXRYTYWXYWZT[K[
  503 19H]ZKYIWGUFQFOGMILKKNKSLVMXOZQ[U[WZYXZV
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each line (ignoring line wrapping) defines a symbol, including the symbol number, number of points, left and right boundaries of the symbol, and finally the individual coordinates.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;709&quot; height=&quot;382&quot; src=&quot;https://breq.dev/_astro/hershey-format.Cw6DS8wN_ZkpXBG.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can think of these coordinates as being given in a coordinate system where the center is given by &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mtext mathvariant=&quot;monospace&quot;&gt;R&lt;/mtext&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mtext mathvariant=&quot;monospace&quot;&gt;R&lt;/mtext&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(\texttt{R}, \texttt{R})&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord texttt&quot;&gt;R&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord texttt&quot;&gt;R&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, with each row below or column to the right getting the next letter, and each row above or column to the left getting the previous letter.&lt;/p&gt;
&lt;p&gt;The line above describes symbol &lt;code&gt;501&lt;/code&gt;, which is made up of nine total points and extends from column &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mtext mathvariant=&quot;monospace&quot;&gt;I&lt;/mtext&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\texttt{I}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6111em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord texttt&quot;&gt;I&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; to column &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mtext mathvariant=&quot;monospace&quot;&gt;[&lt;/mtext&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\texttt{[}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7778em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord texttt&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. The symbol consists of three lines, separated by “pen up” commands.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;626&quot; height=&quot;780&quot; src=&quot;https://breq.dev/_astro/hershey-grid._W10ALZ2_Z2qjLWX.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I find it easiest to envision these symbols as being plotted on a grid of letters. Usually, however, you will treat each “letter” as an ASCII offset from the letter &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mtext mathvariant=&quot;monospace&quot;&gt;R&lt;/mtext&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\texttt{R}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6111em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord texttt&quot;&gt;R&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, like this:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot; data-language=&quot;cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token char&quot;&gt;&apos;I&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token char&quot;&gt;&apos;R&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;
&lt;span class=&quot;token char&quot;&gt;&apos;[&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token char&quot;&gt;&apos;R&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see in this example, the lines are not placed in the best order for efficient galvo scanning. This is one of the major drawbacks of Hershey fonts.&lt;/p&gt;
&lt;p&gt;The second piece of the puzzle is font mapping files. Hershey fonts give each symbol a numeric identifier, but this is unique to the Hershey data — a separate mapping file is required to convert it to ASCII.&lt;/p&gt;
&lt;h2 id=&quot;borland-graphics-interface&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#borland-graphics-interface&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Borland Graphics Interface&lt;/h2&gt;
&lt;p&gt;A variety of vector fonts were shipped with the Borland Graphics Interface, which then made their way into &lt;a href=&quot;https://github.com/gandrewstone/GameMaker/&quot;&gt;GameMaker&lt;/a&gt;, &lt;a href=&quot;https://github.com/apsteinmetz/turboPascal&quot;&gt;Turbo Pascal&lt;/a&gt;, and other frameworks. Via those frameworks, this small set of fonts made its way into a large volume of software. One famous example is the &lt;code&gt;LITT.CHR&lt;/code&gt; font, which became the default font in EAGLE, a popular PCB design program. This was actually how I discovered BGI fonts — through reading &lt;a href=&quot;https://design.astridbin.com/project/little-character&quot;&gt;Astrid’s excellent post about recreating the LITT.CHR font for modern software&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Borland developed a variety of software development tools, including the aforementioned Turbo Pascal (launched in 1983) and the similarly-named Turbo C (in 1987). Both were compilers intended to run on CP/M and, later, MS-DOS. The Borland Graphics Interface was developed to provide a convenient graphics library for software written using Borland’s compilers. It was notable for supporting a wide range of graphics adapters.&lt;/p&gt;
&lt;p&gt;While I was able to find the source files for each of the Borland fonts through the (now MIT-licensed) GameMaker source release, I struggled to find documentation for the font format. The best resource I found was &lt;a href=&quot;https://www.fileformat.info/format/borland-chr/corion.htm&quot;&gt;this page on FileFormat.info&lt;/a&gt;, which seemed to be mostly accurate, but there was still plenty of trial and error in writing a parser.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;.CHR&lt;/code&gt; format has some interesting quirks that definitely reflect the time period it was created in. For instance, to save space, each coordinate pair is written using only 2 bytes by encoding each value as a 7-bit twos complement integer, then using the highest bit of each value to carry additional information:&lt;/p&gt;
&lt;pre class=&quot;language-txt&quot; data-language=&quot;txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;The individual character definitions consist of a variable number of words
describing the operations required to render a character. Each word
consists of an (x,y) coordinate pair and a two-bit opcode, encoded as shown
here:

Byte 1          7   6   5   4   3   2   1   0     bit #
			   op1  &amp;#x3C;seven bit signed X coord&gt;

Byte 2          7   6   5   4   3   2   1   0     bit #
			   op2  &amp;#x3C;seven bit signed Y coord&gt;

		  Opcodes

		op1=0  op2=0  End of character definition.
		op1=0  op2=1  Do scan
		op1=1  op2=0  Move the pointer to (x,y)
		op1=1  op2=1  Draw from current pointer to (x,y)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With a bit of bit manipulation, we can transform this format into a series of points for each glyph. A separate table in the font file stores the width of each character.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;776&quot; height=&quot;510&quot; src=&quot;https://breq.dev/_astro/borland-format.eymQIqF4_ZRhQio.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;newstroke&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#newstroke&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;NewStroke&lt;/h2&gt;
&lt;p&gt;One of the most recent additions to the vector font family is &lt;a href=&quot;https://vovanium.ru/sledy/newstroke/en&quot;&gt;NewStroke&lt;/a&gt;, a font designed for KiCAD. Interestingly, instead of being designed in a more common type of CAD software, NewStroke actually seems to have been designed inside the KiCAD Footprint Editor. I am not sure why this was done.&lt;/p&gt;
&lt;p&gt;Since each character is a footprint, pin definitions are used to specify metrics such as the left and right boundary of the character.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2264&quot; height=&quot;1664&quot; src=&quot;https://breq.dev/_astro/newstroke_footprint.BudQrJaT_1vnr35.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The glyphs are are defined in a &lt;code&gt;.lib&lt;/code&gt; file (not the modern &lt;code&gt;.kicad_sym&lt;/code&gt; format), where each glyph looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;794&quot; height=&quot;452&quot; src=&quot;https://breq.dev/_astro/newstroke-format.DYnD-Lwd_Z20SLus.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Thankfully, there are not that many directives to handle! Page 8 of the &lt;a href=&quot;https://dev-docs.kicad.org/en/file-formats/legacy-4-to-6/legacy_file_format_documentation.pdf&quot;&gt;KiCAD Legacy File Format Documentation&lt;/a&gt; gives detail for each one. We can write a pretty simple parser for this format.&lt;/p&gt;
&lt;p&gt;Of course, we still need something to map these symbol names to Unicode codepoints. We can do this using the &lt;code&gt;charlist.txt&lt;/code&gt; file that is provided alongside the font library:&lt;/p&gt;
&lt;pre class=&quot;language-txt&quot; data-language=&quot;txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;# symbol list

startchar 32
font newstroke_font
// BASIC LATIN (0020-007F)
+ SPACE
+ EXCLAM
+ QUOTE
+ HASH
+ S_CAP LINE_V_CAP
+ PERCENT
+ AMPERSAND
+ APOSTROPHE
+ PAREN
+ !PAREN
+ ASTERISK
+ PLUS
+ COMMA
+ MINUS
+ FULL_STOP
+ SLASH
+ DIGIT_0
+ DIGIT_1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each line corresponds to a single Unicode codepoint. Notably, some directives are used (&lt;code&gt;!&lt;/code&gt; to invert the parenthesis character, &lt;code&gt;+&lt;/code&gt; to combine characters, etc) to reduce the amount of symbols required to generate the font. Fancier characters will be composed of multiple glyph parts, anchored together using additional pin definitions contained with each symbol.&lt;/p&gt;
&lt;p&gt;The original NewStroke project used an &lt;a href=&quot;https://gitlab.com/kicad/code/kicad/-/blob/afd432e687b80f02035e337c2d1d3b9578835211/helpers/tools_to_build_newstroke-font/fontconv.awk&quot;&gt;AWK script&lt;/a&gt; to transpile the original font definitions into a C source code file containing Hershey-style glyph definitions. I was able to use this AWK code as a resource when writing my own parser.&lt;/p&gt;
&lt;h2 id=&quot;rust-build-scripts&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#rust-build-scripts&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Rust Build Scripts&lt;/h2&gt;
&lt;p&gt;When writing parsers for these formats, I encountered the problem of figuring out how to efficiently store and retrieve font data from within a program. It became clear early on that the formats these fonts were originally developed in were not ideal for reading in at runtime — some made looking up a character by index excessively slow, and others made inefficient use of space.&lt;/p&gt;
&lt;p&gt;While I thought about using macros to generate code based on the font definitions, I worried that the macro syntax would be overly restrictive. For this reason, I decided to use a build script to parse the font files at compile time and create the glyph tables.&lt;/p&gt;
&lt;p&gt;Crates in Rust can provide a &lt;code&gt;build.rs&lt;/code&gt; script which is invoked before the crate is compiled. In my case, I use this to generate files at runtime with font data.&lt;/p&gt;
&lt;h2 id=&quot;packaging&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#packaging&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Packaging&lt;/h2&gt;
&lt;p&gt;One of the things I noticed when starting this project was that support for stroke vector font formats seemed limited in existing libraries. I decided early on that I wanted to publish these parsers as a package for others to use in their projects.&lt;/p&gt;
&lt;p&gt;I decided to publish separate crates for each format, in order to keep all of the logic in the build scripts separate, then create a “wrapper” crate which provided each of the available fonts.&lt;/p&gt;
&lt;p&gt;This was my first experience publishing a crate to &lt;code&gt;crates.io&lt;/code&gt;! I would say it went quite smoothly, and the login and publishing flow took me very little time at all.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The library is available on &lt;a href=&quot;https://crates.io/crates/vector-text&quot;&gt;crates.io&lt;/a&gt;, give it a try! Feel free to &lt;a href=&quot;https://github.com/breqdev/vector-text/issues&quot;&gt;open an issue&lt;/a&gt; if you find any issues or have ideas for improvements.&lt;/p&gt;
&lt;p&gt;I have some plans in the works involving a vector display, and I made this project to give myself an easy way to swap out different fonts of different formats for testing. I’m quite happy with the API I was able to create!&lt;/p&gt;
&lt;p&gt;Writing parsers like this is always an interesting challenge that I often don’t get to take on. I hope this library is useful to anyone else attempting a similar project. I definitely expect aspects of it to evolve as I begin using it more.&lt;/p&gt;</content:encoded></item><item><title>Infinite Coffee Glitch</title><link>https://breq.dev/2026/01/14/infinite-coffee-glitch/</link><guid isPermaLink="true">https://breq.dev/2026/01/14/infinite-coffee-glitch/</guid><description>FREE COFFEE HACK 2026 WORKING [FREE DOWNLOAD]</description><pubDate>Wed, 14 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;div class=&quot;callout-gray&quot;&gt;
&lt;h2 class=&quot;font-bold text-2xl mt-4&quot;&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://terminal.shop&quot;&gt;terminal.shop&lt;/a&gt; is an online store selling coffee beans via an SSH-based interface. They also provide an API allowing users to place orders via HTTP requests. The &lt;code&gt;/order&lt;/code&gt; endpoint had inadequate validation, allowing for nonsensical orders including empty carts, items with quantity 0, and more interestingly: &lt;strong&gt;the ability to get coffee for free by adding negative quantity items to your cart.&lt;/strong&gt; All you had to do is call this endpoint with a quantity of -1 for an item and $-22 would be deducted from your order total.&lt;/p&gt;
&lt;p&gt;The vulnerability was responsibly disclosed and has been patched. (But even though you can no longer get coffee for free, please still check them out as they sell great products at reasonable prices!)&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;My girlfriends and I are big coffee enjoyers, and since getting an espresso machine in 2024, we’ve gotten deep into trying different types of coffee beans in lattes and other drinks. So when I stumbled upon &lt;a href=&quot;https://terminal.shop/&quot;&gt;terminal.shop&lt;/a&gt; last summer, I figured it would be a fun gimmick and a way to try out some different beans. The process is pretty simple: SSH into &lt;code&gt;terminal.shop&lt;/code&gt; and use your keyboard to navigate the menus and order yourself some coffee.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1364&quot; height=&quot;1012&quot; src=&quot;https://breq.dev/_astro/storefront.HBxh5m9p_IQ7Cj.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Running a storefront over SSH works really well! Pages load almost instantly (since they’re just an 80x24 grid of characters). While you don’t have the full abilities of CSS, they’ve made extensive use of &lt;a href=&quot;https://en.wikipedia.org/wiki/ANSI_escape_code&quot;&gt;ANSI codes&lt;/a&gt; to create an aesthetically pleasing interface. Unlike HTTPS, SSH doesn’t have public key infrastructure — there’s no certificate authority asserting that the terminal.shop server is legitimate. However, they put their SSH public key on their website so you can verify it yourself, and once you login the first time, your client will store the fingerprint to ensure the server is legitimate on future logins.&lt;/p&gt;
&lt;p&gt;While we placed our first order because buying something over SSH sounded too fun to pass up, we ended up really liking the decaf and dark roast. However, once we become semi-regular customers, ordering via SSH lost its initial novelty, and I wanted to try something new.&lt;/p&gt;
&lt;h2 id=&quot;none-coffee-with-left-shipping&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#none-coffee-with-left-shipping&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;None Coffee with Left Shipping&lt;/h2&gt;
&lt;p&gt;terminal.shop provides an &lt;a href=&quot;https://www.terminal.shop/api&quot;&gt;HTTP API&lt;/a&gt; that allows you to place orders. They also have a bunch of &lt;a href=&quot;https://www.terminal.shop/api#client-sdks&quot;&gt;client libraries&lt;/a&gt;, but I found the bare HTTP documentation easier to read, so I set out to write a Bash script so I could place orders from my terminal without the hassle of a TUI. My goal was to have one command I could run any time we needed to stock up.&lt;/p&gt;
&lt;p&gt;Unfortunately, the development backend at &lt;code&gt;api.dev.terminal.shop&lt;/code&gt; was down when I tried to test my code, so I sighed, took out my real credit card, crossed my fingers and hit enter. I immediately got a notification that I had been charged $8 and hadn’t received a confirmation email. After a bit more digging, I realized where I had gone wrong — I had supplied the &lt;em&gt;product ID&lt;/em&gt; of each product I wanted to buy instead of the &lt;em&gt;product variant ID&lt;/em&gt;, causing the order to be created with an empty cart, myself to be billed $8 for shipping and $0 for my nonexistent items, and then (presumably) the backend crashing later when trying to generate my order confirmation.&lt;/p&gt;
&lt;div class=&quot;border-gray-400 border-2 max-w-[min(36rem,100%)] print:max-w-sm overflow-clip rounded-2xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1006&quot; height=&quot;834&quot; src=&quot;https://breq.dev/_astro/support-email.bWqsF_wO_Z1fX0T3.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Immediately after I realized this, I sent an email to &lt;code&gt;support@terminal.shop&lt;/code&gt;. I didn’t get a response and, for a time, resigned myself to the fact that I had paid $8 for an interesting story. (I don’t blame them for not getting back to me, Terminal Products seems to be a side project of a few content creators and I’m sure that job can be incredibly hectic.)&lt;/p&gt;
&lt;h2 id=&quot;bash-hacking&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#bash-hacking&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Bash hacking&lt;/h2&gt;
&lt;p&gt;I patched my code and ordered again. This time, it was a success! However, something was up with the packing slip…&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-[1fr,2fr] gap-2 max-w-4xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2800&quot; height=&quot;3733&quot; src=&quot;https://breq.dev/_astro/packing-slip-002.BivFYMLg_1dEk2q.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1886&quot; height=&quot;904&quot; src=&quot;https://breq.dev/_astro/order-002.CUvIOwIB_Z1GQ01a.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;All four items in the shop were present in both the email and the packing slip, but the two I did not order had a quantity set to zero. This was a byproduct of how my ordering script worked: instead of adding each product to the payload when a user ordered it, I always put all four available variant IDs into the payload with a quantity set to zero unless that argument was set to zero.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# usage: ./order.sh --segfault 1 --404 1&lt;/span&gt;

&lt;span class=&quot;token assign-left variable&quot;&gt;OBJECT_OBJECT_QTY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;SEGFAULT_QTY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;DARK_MODE_QTY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;_404_QTY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$#&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-gt&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;
    --object-object&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token assign-left variable&quot;&gt;OBJECT_OBJECT_QTY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;shift&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    --segfault&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token assign-left variable&quot;&gt;SEGFAULT_QTY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;shift&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    --dark-mode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token assign-left variable&quot;&gt;DARK_MODE_QTY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;shift&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    --404&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token assign-left variable&quot;&gt;_404_QTY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;shift&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    *&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Unknown option: &lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;esac&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;


&lt;span class=&quot;token assign-left variable&quot;&gt;ORDER_PAYLOAD&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;jq &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;{
    addressID: &quot;&apos;&lt;/span&gt;$SAVED_ADDRESS_ID&lt;span class=&quot;token string&quot;&gt;&apos;&quot;,
    cardID: &quot;&apos;&lt;/span&gt;$SAVED_CARD_ID&lt;span class=&quot;token string&quot;&gt;&apos;&quot;,
    variants: {
        &quot;&apos;&lt;/span&gt;$ITEM_OBJECT_OBJECT&lt;span class=&quot;token string&quot;&gt;&apos;&quot;: &apos;&lt;/span&gt;$OBJECT_OBJECT_QTY&lt;span class=&quot;token string&quot;&gt;&apos;,
        &quot;&apos;&lt;/span&gt;$ITEM_SEGFAULT&lt;span class=&quot;token string&quot;&gt;&apos;&quot;: &apos;&lt;/span&gt;$SEGFAULT_QTY&lt;span class=&quot;token string&quot;&gt;&apos;,
        &quot;&apos;&lt;/span&gt;$ITEM_DARK_MODE&lt;span class=&quot;token string&quot;&gt;&apos;&quot;: &apos;&lt;/span&gt;$DARK_MODE_QTY&lt;span class=&quot;token string&quot;&gt;&apos;,
        &quot;&apos;&lt;/span&gt;$ITEM_404&lt;span class=&quot;token string&quot;&gt;&apos;&quot;: &apos;&lt;/span&gt;$_404_QTY&lt;span class=&quot;token string&quot;&gt;&apos;,
    }
}&apos;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This was a purely arbitrary choice I made since I thought the ergonomics of doing it this way was easier in Bash versus trying to conditionally add things to the JSON payload. I figured that the backend would just filter out any items with a quantity of zero before the order processed. But it seemed like those “quantity zero” items were still there!&lt;/p&gt;
&lt;h2 id=&quot;just-how-much-does-this-endpoint-allow&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#just-how-much-does-this-endpoint-allow&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Just how much does this endpoint allow?&lt;/h2&gt;
&lt;p&gt;An idea dawned on us. It seemed like this endpoint had pretty poor validation overall. If we placed an order for the 2 types of coffee we wanted, then ordered -1 of a coffee we didn’t care about, maybe we could receive these items for less money and thus get back the $8 we lost earlier!&lt;/p&gt;
&lt;p&gt;We were almost certain that it wouldn’t work, but I ran the script again:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;./order.sh &lt;span class=&quot;token parameter variable&quot;&gt;--404&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; --dark-mode &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--segfault&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Verified the payload it created:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot; data-language=&quot;json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;addressID&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;shp_XXXXXXXXXXXXXXXXXXXXXXXXXX&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;cardID&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;crd_XXXXXXXXXXXXXXXXXXXXXXXXXX&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;variants&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;var_01J1JFE53306NT180RC4HGPWH8&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;var_01J1JFDMNBXB5GJCQF6C3AEBCQ&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;-1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;var_01J1JFF4D5PBGT0W2RJ7FREHRR&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;var_01J1JFEP8WXK5MKXNBTR2FJ1YC&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then sent it off, and watched the response come in:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot; data-language=&quot;json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ord_XXXXXXXXXXXXXXXXXXXXXXXXXX&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We successfully created an order! Just like that, we got a notification that the card had been charged $30! Then, checking my email, I saw the order confirmation also listed payment of $30: 1 bag of 404 at $22, 1 bag of Dark Mode at $22, 0 bags of [object Object] at $0, and -1 bags of Segfault for $-22.&lt;/p&gt;
&lt;div class=&quot;border-gray-400 border-2 max-w-[min(48rem,100%)] print:max-w-sm overflow-clip rounded-2xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1894&quot; height=&quot;900&quot; src=&quot;https://breq.dev/_astro/order-003.BkZE81fK_ZAthg7.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;We waited patiently for the tracking number. Once it arrived, we checked the metadata, and the shipping label listed the package as weighing 12 oz — the weight of one bag. It would seem, somehow, our order got corrected and we were receiving only the one bag we had paid for.&lt;/p&gt;
&lt;p&gt;Until… a box showed up outside our apartment! I picked it up and noticed it weighed far more than a single bag. Once I opened it, I realized we had been sent all four types of coffee!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3072&quot; height=&quot;4096&quot; src=&quot;https://breq.dev/_astro/packing-slip-003.DuWONfY6_ZbEWcQ.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;It appears the order was able to make it all the way to the human fulfilling it without being filtered out, who then probably saw the packing list, thought “oh, I guess the quantity column is messed up on this one,” and packed and shipped it off.&lt;/p&gt;
&lt;p&gt;It quickly dawned on us what we had actually done: paid $30 for $88 worth of coffee and put Terminal Products in the position of shipping a 48 oz package with a label that said 12 oz. Not ideal!&lt;/p&gt;
&lt;div class=&quot;border-gray-400 border-2 max-w-[min(48rem,100%)] print:max-w-sm overflow-clip rounded-2xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;962&quot; height=&quot;670&quot; src=&quot;https://breq.dev/_astro/discord.8N2sM4cQ_Z1rpbwR.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;aftermath&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#aftermath&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Aftermath&lt;/h2&gt;
&lt;p&gt;As it turns out, my girlfriend Ava realized that she unexpectedly shares a mutual acquaintance with some of the terminal.shop team, so word reached them pretty quickly.&lt;/p&gt;
&lt;tweet id=&quot;2010871723588288722&quot;&gt;
&lt;p&gt;Thankfully, the folks at Terminal were super chill about this and quickly patched the issue. Not only do I get to keep four times as much coffee as I expected, but we were given a reward for reporting this in the form of even more coffee. (Friends in Boston, please hit me up if you want a latte sometime!)&lt;/p&gt;
&lt;p&gt;From what I can tell, everything exploitable via bare HTTP requests would be similar to accomplish with the official client libraries. The only “trick” was using the API, calling the &lt;code&gt;/order&lt;/code&gt; endpoint directly instead of adding items through &lt;code&gt;/cart/item&lt;/code&gt;, and just putting weird stuff in the &lt;code&gt;variants&lt;/code&gt; field.&lt;/p&gt;
&lt;p&gt;It was fun to watch the hypotheses come in once this was announced online: was it an obscure SSH feature? Something with &lt;code&gt;SendEnv&lt;/code&gt;? Terminal control characters?&lt;/p&gt;
&lt;p&gt;These explanations were all unlikely. You might think terminal.shop is built on a traditional server like OpenSSH, with the login shell set to a TUI program, which would thus leave it open to bugs or misconfigurations in a wide range of obscure SSH features. However, the answer is much nicer — the terminal.shop TUI is built with &lt;a href=&quot;https://github.com/charmbracelet/wish&quot;&gt;Wish&lt;/a&gt;, a framework by &lt;a href=&quot;https://charm.land/&quot;&gt;Charm&lt;/a&gt; that allows you to create apps accessible over SSH without ever creating an actual shell. It’s the same framework I used for &lt;a href=&quot;https://breq.dev/projects/fissh&quot;&gt;fissh.breq.dev&lt;/a&gt;, a tiny app I made about a year ago with Ava that presents you with an ASCII drawing of a fish every day at 11:11.&lt;/p&gt;
&lt;p&gt;Discovering this vulnerability was a long adventure in the making! I want to thank my girlfriends &lt;a href=&quot;https://avasilver.dev&quot;&gt;Ava&lt;/a&gt; and &lt;a href=&quot;https://miakizz.quest&quot;&gt;Mia&lt;/a&gt; for encouraging me and offering advice, &lt;a href=&quot;https://aaronstuyvenberg.com/&quot;&gt;AJ Stuyvenberg&lt;/a&gt; for getting us connected to the team at Terminal, and of course all the folks at Terminal Products for having an open and positive attitude towards security research. Sometimes, the most powerful bugs are the ones that require the least complicated exploits!&lt;/p&gt;&lt;/tweet&gt;</content:encoded></item><item><title>Parsing historical MBTA data</title><link>https://breq.dev/2025/11/02/historical-mbta/</link><guid isPermaLink="true">https://breq.dev/2025/11/02/historical-mbta/</guid><description>Buses and trains, now with time travel</description><pubDate>Sun, 02 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;transit-and-data&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#transit-and-data&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Transit and Data&lt;/h1&gt;
&lt;p&gt;Transit systems and public data are a great match. In my daily life, I interact with so many devices and applications which pull from transit data, from the &lt;a href=&quot;https://breq.dev/projects/matrix2&quot;&gt;LED matrix in my living room&lt;/a&gt;, to the app on my phone, and to the countdown clocks within the station itself. It’s also incredibly accessible, too, as the myriad of DIY projects pulling in transit data demonstrates.&lt;/p&gt;
&lt;p&gt;There are, in general, two types of data about a transit system: what the system is in &lt;em&gt;theory&lt;/em&gt; (schedules, routes, and stations determined months in advance), and what the system is in &lt;em&gt;practice&lt;/em&gt; (vehicle locations, arrival predictions, and dropped/added trips updated in realtime).&lt;/p&gt;
&lt;h2 id=&quot;transit-systems-in-theory&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#transit-systems-in-theory&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Transit Systems in Theory&lt;/h2&gt;
&lt;p&gt;Describing what a transit system does in theory is the easy part. The &lt;a href=&quot;https://gtfs.org/&quot;&gt;General Transit Feed Specification&lt;/a&gt; defines a common format made up (in true 2006 fashion) of a set of TXT files contained within a ZIP file, each describing a different aspect of the system. For instance, &lt;code&gt;stops.txt&lt;/code&gt; describes the vehicle stops and stations in the system, &lt;code&gt;stop_times.txt&lt;/code&gt; describes when each stop is serviced by a vehicle trip, and &lt;code&gt;trips.txt&lt;/code&gt; can specify the train number, whether bikes are aloud, and more.&lt;/p&gt;
&lt;p&gt;The benefit of this approach is clear: sharing a common standard means that code written for one city can work seamlessly with others. Smaller transit operators can create these files by hand, and larger ones can build up the necessary automation to handle hundreds of routes and thousands of stops. Since these ZIP files usually only change when new schedules are determined, distributing them is straightforward and storing historical ones is easy to do.&lt;/p&gt;
&lt;h2 id=&quot;transit-systems-in-practice&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#transit-systems-in-practice&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Transit Systems in Practice&lt;/h2&gt;
&lt;p&gt;Realtime data is where things get messy. The requirements are more demanding. Data usually needs to be generated and distributed without a human in the loop, and clients need to pull updates every minute or faster.&lt;/p&gt;
&lt;p&gt;GTFS does offer a solution to this in the form of &lt;a href=&quot;https://gtfs.org/documentation/realtime/reference/&quot;&gt;GTFS-Realtime&lt;/a&gt;. However, being a Google initiative, they chose to build this using &lt;a href=&quot;https://protobuf.dev/&quot;&gt;Protobuf&lt;/a&gt; as an interchange format, which requires specific language bindings to work with. My home system, the MBTA, chose to offer a &lt;a href=&quot;https://github.com/mbta/gtfs-documentation/blob/master/reference/gtfs-realtime.md#json-feeds&quot;&gt;JSON version&lt;/a&gt; of their feeds as well.&lt;/p&gt;
&lt;p&gt;Even still, I tend to use their excellent, well-documented &lt;a href=&quot;https://api-v3.mbta.com/docs/swagger/index.html&quot;&gt;service API&lt;/a&gt; which makes it easier to ingest only the data relevant to the lines and stations I need. Interoperability with other systems is usually not a priority for my projects.&lt;/p&gt;
&lt;p&gt;GTFS-Realtime, however, does not specify how &lt;em&gt;historical&lt;/em&gt; data should be represented and stored, leaving transit systems to invent bespoke formats for this data — that is, if they choose to make it available at all.&lt;/p&gt;
&lt;h1 id=&quot;historical-data-at-the-mbta&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#historical-data-at-the-mbta&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Historical Data at the MBTA&lt;/h1&gt;
&lt;h2 id=&quot;lamp&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#lamp&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;LAMP&lt;/h2&gt;
&lt;p&gt;The MBTA has a service called &lt;a href=&quot;https://performancedata.mbta.com/&quot;&gt;LAMP&lt;/a&gt; (Lightweight Application for Measuring Performance), which does three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Publish historical GTFS schedule data, showing the state of the system “in theory” for any arbitrary date since 2009.&lt;/li&gt;
&lt;li&gt;Publish historical subway performance data (the system “in practice”) sorted by service date.&lt;/li&gt;
&lt;li&gt;Publish miscellaneous datasets for MBTA-internal uses (while these are public, they are entirely undocumented).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That second point is what we’ll focus on for parsing historical realtime data.&lt;/p&gt;
&lt;h2 id=&quot;open-data-portal&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#open-data-portal&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Open Data Portal&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://mbta-massdot.opendata.arcgis.com/&quot;&gt;MBTA Open Data Portal&lt;/a&gt; contains lots of additional reports generated by the MBTA covering ridership, predictions accuracy, and more across the various transit modes. One such dataset is the &lt;a href=&quot;https://mbta-massdot.opendata.arcgis.com/datasets/924df13d845f4907bb6a6c3ed380d57a/about&quot;&gt;Bus Arrival Departure Times 2025&lt;/a&gt; dataset, which nicely complements the subway data published by the LAMP team.&lt;/p&gt;
&lt;h1 id=&quot;lets-get-parsing&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#lets-get-parsing&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Let’s get parsing&lt;/h1&gt;
&lt;h2 id=&quot;subway-data&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#subway-data&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Subway data&lt;/h2&gt;
&lt;h3 id=&quot;data-format&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#data-format&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Data format&lt;/h3&gt;
&lt;p&gt;The subway data we’re interested in is distributed in the Parquet format, using URLs like this for each day of service:&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;https://performancedata.mbta.com/lamp/subway-on-time-performance-v1/YYYY-MM-DD-subway-on-time-performance-v1.parquet
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href=&quot;https://parquet.apache.org/&quot;&gt;Parquet file format&lt;/a&gt; is an Apache project specification for storing tabular data efficiently. They pack the column data efficiently but don’t require the receiver to know the schema definition like Protobuf does, which means we can easily throw these files into &lt;a href=&quot;https://pandas.pydata.org/&quot;&gt;Pandas&lt;/a&gt;, the popular Python data analysis library.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; pandas &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; pd

path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://performancedata.mbta.com/lamp/subway-on-time-performance-v1/2025-10-31-subway-on-time-performance-v1.parquet&quot;&lt;/span&gt;
df &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read_parquet&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data.json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; out&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;write&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;to_json&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;orient&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;records&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can see that the data has 27 columns. While these aren’t documented anywhere, here’s how I assume the data is structured:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each entry describes a vehicle arriving and/or departing a station as part of a revenue trip.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stop_id&lt;/code&gt; and &lt;code&gt;parent_station&lt;/code&gt; describe which platform and station the vehicle was at: &lt;code&gt;stop_id&lt;/code&gt; identifies the platform (for instance, &lt;code&gt;70513&lt;/code&gt; means the northbound platform at &lt;em&gt;East Somerville&lt;/em&gt;), and &lt;code&gt;parent_station&lt;/code&gt; describes the station it belongs to (in this case, &lt;code&gt;place-esomr&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;move_timestamp&lt;/code&gt; seems to be when the train starting moving &lt;em&gt;towards&lt;/em&gt; the given station, and &lt;code&gt;stop_timestamp&lt;/code&gt; is when it reached the station.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;travel_time_seconds&lt;/code&gt; seems to be the amount of time it took the train to reach the given station, and &lt;code&gt;dwell_time_seconds&lt;/code&gt; is how long it spent there.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;service_date&lt;/code&gt; describes the service date as an integer with a decimal expansion of the form YYYYMMDD… bruh&lt;/li&gt;
&lt;li&gt;&lt;code&gt;route_id&lt;/code&gt; defines the specific route, such as &lt;code&gt;Blue&lt;/code&gt;, &lt;code&gt;Green-E&lt;/code&gt;, or &lt;code&gt;Red&lt;/code&gt;. &lt;code&gt;branch_route_id&lt;/code&gt; defines the branch, such as &lt;code&gt;Blue&lt;/code&gt; (no branching), &lt;code&gt;Green-E&lt;/code&gt;, or &lt;code&gt;Red-A&lt;/code&gt;. I am unsure why the Red line branching is treated differently than the Green line here.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;direction_id&lt;/code&gt; is either &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;, depending on which way the train is heading. &lt;code&gt;direction&lt;/code&gt; is the human-readable name, like &lt;code&gt;South&lt;/code&gt;. &lt;code&gt;direction_destination&lt;/code&gt; is the direction given as a destination station or station pair, like &lt;code&gt;Ashmont/Braintree&lt;/code&gt; or &lt;code&gt;Boston College&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;start_time&lt;/code&gt; seems to be the time that the vehicle started moving, given as “seconds since midnight at the start of the service day”. Since the MBTA defines “service days” as starting and ending around 3 AM, the first vehicles have a &lt;code&gt;start_time&lt;/code&gt; of around &lt;code&gt;17680&lt;/code&gt; (4:54 AM) and the last ones have a &lt;code&gt;start_time&lt;/code&gt; of around &lt;code&gt;95976&lt;/code&gt; (2:39 AM). &lt;code&gt;stop_count&lt;/code&gt; is the number of stops that vehicle has made since that time, I guess?&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vehicle_id&lt;/code&gt; is a unique identifier for the vehicle, &lt;code&gt;vehicle_label&lt;/code&gt; is a human-readable label (usually the number of the first one or two cars), and &lt;code&gt;vehicle_consist&lt;/code&gt; is the car numbers of each car in the train.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;trip_id&lt;/code&gt; identifies the trip that the vehicle was on.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;simulating-trips&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#simulating-trips&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Simulating trips&lt;/h3&gt;
&lt;p&gt;Suppose I had been on the southbound platform at Sullivan Station at 5:15 PM. When would I have made it to North Station?&lt;/p&gt;
&lt;p&gt;Let’s start by finding all the trips which arrived at North Station coming southbound.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;trips_to_target &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;parent_station&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;place-north&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;direction&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;South&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Stop ID&lt;/th&gt;&lt;th&gt;Parent Station&lt;/th&gt;&lt;th&gt;Move Timestamp&lt;/th&gt;&lt;th&gt;Stop Timestamp&lt;/th&gt;&lt;th&gt;Route ID&lt;/th&gt;&lt;th&gt;Direction&lt;/th&gt;&lt;th&gt;Trip ID&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;70026&lt;/td&gt;&lt;td&gt;place-north&lt;/td&gt;&lt;td&gt;1761902928.0&lt;/td&gt;&lt;td&gt;1761903013.0&lt;/td&gt;&lt;td&gt;Orange&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;70525780&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;70026&lt;/td&gt;&lt;td&gt;place-north&lt;/td&gt;&lt;td&gt;1761903114.0&lt;/td&gt;&lt;td&gt;1761903199.0&lt;/td&gt;&lt;td&gt;Orange&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;70525786&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;70026&lt;/td&gt;&lt;td&gt;place-north&lt;/td&gt;&lt;td&gt;1761903280.0&lt;/td&gt;&lt;td&gt;1761903367.0&lt;/td&gt;&lt;td&gt;Orange&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;70525792&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;70026&lt;/td&gt;&lt;td&gt;place-north&lt;/td&gt;&lt;td&gt;1761903673.0&lt;/td&gt;&lt;td&gt;1761903759.0&lt;/td&gt;&lt;td&gt;Orange&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;70525798&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Now, let’s find the earliest one of those trips which also stopped at Sullivan.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;trip_ids &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; trips_to_target&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;trip_id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;unique&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

trips_from_start &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;parent_station&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;place-sull&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;direction&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;South&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;trip_id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;trip_ids&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Stop ID&lt;/th&gt;&lt;th&gt;Parent Station&lt;/th&gt;&lt;th&gt;Move Timestamp&lt;/th&gt;&lt;th&gt;Stop Timestamp&lt;/th&gt;&lt;th&gt;Route ID&lt;/th&gt;&lt;th&gt;Direction&lt;/th&gt;&lt;th&gt;Trip ID&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;70030&lt;/td&gt;&lt;td&gt;place-sull&lt;/td&gt;&lt;td&gt;1761902684.0&lt;/td&gt;&lt;td&gt;1761902737.0&lt;/td&gt;&lt;td&gt;Orange&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;70525780&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;70030&lt;/td&gt;&lt;td&gt;place-sull&lt;/td&gt;&lt;td&gt;1761902876.0&lt;/td&gt;&lt;td&gt;1761902929.0&lt;/td&gt;&lt;td&gt;Orange&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;70525786&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;70030&lt;/td&gt;&lt;td&gt;place-sull&lt;/td&gt;&lt;td&gt;1761903061.0&lt;/td&gt;&lt;td&gt;1761903110.0&lt;/td&gt;&lt;td&gt;Orange&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;70525792&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;70030&lt;/td&gt;&lt;td&gt;place-sull&lt;/td&gt;&lt;td&gt;1761903454.0&lt;/td&gt;&lt;td&gt;1761903502.0&lt;/td&gt;&lt;td&gt;Orange&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;70525798&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Right now, most of these are the same trip IDs, but now the timestamps match up with the train’s stop at Sullivan. If we were dealing with different branches of the Green Line, for instance, this step would also filter out trips which don’t run between our station pair.&lt;/p&gt;
&lt;p&gt;Finally, let’s find the first trip from the start which departed after we got to the station.&lt;/p&gt;
&lt;p&gt;Times in this data are represented as Unix timestamps (i.e., seconds since 1970), seemingly in the local timezone (U.S. Eastern time). So, for instance, the first trip in our list arrived at the station at &lt;code&gt;1761902737&lt;/code&gt; seconds, or at 5:25:37 AM on 2025-10-31.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; datetime

timestamp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fromisoformat&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2025-10-31 17:15:00&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timestamp&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
trips_after_time &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; trips_from_start&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;trips_from_start&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;stop_timestamp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; timestamp&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Stop ID&lt;/th&gt;&lt;th&gt;Parent Station&lt;/th&gt;&lt;th&gt;Move Timestamp&lt;/th&gt;&lt;th&gt;Stop Timestamp&lt;/th&gt;&lt;th&gt;Route ID&lt;/th&gt;&lt;th&gt;Direction&lt;/th&gt;&lt;th&gt;Trip ID&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;70030&lt;/td&gt;&lt;td&gt;place-sull&lt;/td&gt;&lt;td&gt;1761946007.0&lt;/td&gt;&lt;td&gt;1761946055.0&lt;/td&gt;&lt;td&gt;Orange&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;70526030&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;70030&lt;/td&gt;&lt;td&gt;place-sull&lt;/td&gt;&lt;td&gt;1761946114.0&lt;/td&gt;&lt;td&gt;1761946163.0&lt;/td&gt;&lt;td&gt;Orange&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;70526038&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;70030&lt;/td&gt;&lt;td&gt;place-sull&lt;/td&gt;&lt;td&gt;1761946438.0&lt;/td&gt;&lt;td&gt;1761946487.0&lt;/td&gt;&lt;td&gt;Orange&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;70526046&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;70030&lt;/td&gt;&lt;td&gt;place-sull&lt;/td&gt;&lt;td&gt;1761946646.0&lt;/td&gt;&lt;td&gt;1761946693.0&lt;/td&gt;&lt;td&gt;Orange&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;70526054&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The first of those trips is trip ID &lt;code&gt;70526030&lt;/code&gt;, which arrived at Sullivan at 5:27:35 PM.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;next_train &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; trips_after_time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;loc&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;trips_after_time&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;stop_timestamp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;idxmin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

train_arrival &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;trip_id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; next_train&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;trip_id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;parent_station&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;place-north&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looking up its arrival into North Station, we see:&lt;/p&gt;























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Stop ID&lt;/th&gt;&lt;th&gt;Parent Station&lt;/th&gt;&lt;th&gt;Move Timestamp&lt;/th&gt;&lt;th&gt;Stop Timestamp&lt;/th&gt;&lt;th&gt;Route ID&lt;/th&gt;&lt;th&gt;Direction&lt;/th&gt;&lt;th&gt;Trip ID&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;70026&lt;/td&gt;&lt;td&gt;place-north&lt;/td&gt;&lt;td&gt;1761946237.0&lt;/td&gt;&lt;td&gt;1761946322.0&lt;/td&gt;&lt;td&gt;Orange&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;70526030&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;It appears we would have arrived at 5:32:02 PM.&lt;/p&gt;
&lt;h2 id=&quot;bus-data&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#bus-data&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Bus data&lt;/h2&gt;
&lt;p&gt;The bus data is a little different. The MBTA provides it as a ZIP file for each year, with a CSV file for each month. At time of writing, the latest one is &lt;code&gt;MBTA-Bus-Arrival-Departure-Times_2025-09.csv&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The data is pretty straightforward, providing the following columns:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;service_date&lt;/code&gt; is self-explanatory.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;route_id&lt;/code&gt; is the bus number. Note that for some buses, the internal route ID does not match the consumer-facing bus number. For instance, route &lt;a href=&quot;https://www.mbta.com/schedules/8993/line&quot;&gt;89/93&lt;/a&gt; is identified as 194 internally, and the first &lt;a href=&quot;https://www.mbta.com/schedules/39/line&quot;&gt;39&lt;/a&gt; bus of the day is considered route 192 internally.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;direction_id&lt;/code&gt; is the direction identifier, which appears to only ever be &lt;code&gt;Outbound&lt;/code&gt; or &lt;code&gt;Inbound&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;half_trip_id&lt;/code&gt; is like the &lt;code&gt;trip_id&lt;/code&gt; of the subway data, but since the outbound and inbound trip of a bus route often share a trip ID, this disambiguates them.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stop_id&lt;/code&gt; is the identifier of the platform at which the bus stops. Large stations like Sullivan provide many bus platforms, each with its own ID.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;point_type&lt;/code&gt; specifies if this stop is a &lt;code&gt;Startpoint&lt;/code&gt;, &lt;code&gt;Midpoint&lt;/code&gt;, or &lt;code&gt;Endpoint&lt;/code&gt; of the route.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scheduled&lt;/code&gt; and &lt;code&gt;actual&lt;/code&gt; give the scheduled and actual arrival times for the bus, respectively. However, for some reason, the date format is of the form &lt;code&gt;1900-01-01T14:28:00Z&lt;/code&gt;… We’ll dig into this more later.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notably, not every stop is covered in this data, only the &lt;em&gt;time points&lt;/em&gt; (places along the route with a scheduled arrival time). However, the time points are typically placed at high-traffic stops like subway stations or the start and end of the line, so they are probably useful anyway.&lt;/p&gt;
&lt;h3 id=&quot;basic-parsing&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#basic-parsing&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Basic parsing&lt;/h3&gt;
&lt;p&gt;Let’s see if we can repeat a similar “simulated journey” to what we did with the subway, but with the bus data. Suppose I’m trying to get from Harvard to Hynes Convention Center station using the 1 bus on 2025-09-20 at 11:00 AM.&lt;/p&gt;
&lt;p&gt;Ingesting the CSV file is quite easy:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; pandas &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; pd

path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MBTA_Bus_Arrival_Departure_Times_2025/MBTA-Bus-Arrival-Departure-Times_2025-09.csv&quot;&lt;/span&gt;
df &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read_csv&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first part is quite similar to simulating a subway trip. Let’s try selecting all of the trips that arrived at our destination stop ID, stop &lt;a href=&quot;https://www.mbta.com/stops/79&quot;&gt;79&lt;/a&gt;, then find the records for those trips departing from Harvard, stop &lt;a href=&quot;https://www.mbta.com/stops/110&quot;&gt;110&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;trips_to_target &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;stop_id&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;79&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;direction_id&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Inbound&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

trip_ids &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; trips_to_target&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;half_trip_id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;unique&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

trips_from_start &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;stop_id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;110&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;direction_id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Inbound&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;half_trip_id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;trip_ids&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Service Date&lt;/th&gt;&lt;th&gt;Route ID&lt;/th&gt;&lt;th&gt;Direction ID&lt;/th&gt;&lt;th&gt;Half Trip ID&lt;/th&gt;&lt;th&gt;Stop ID&lt;/th&gt;&lt;th&gt;Scheduled&lt;/th&gt;&lt;th&gt;Actual&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;2025-09-01&lt;/td&gt;&lt;td&gt;01&lt;/td&gt;&lt;td&gt;Inbound&lt;/td&gt;&lt;td&gt;68099570&lt;/td&gt;&lt;td&gt;110&lt;/td&gt;&lt;td&gt;1900-01-01T11:31:00Z&lt;/td&gt;&lt;td&gt;1900-01-01T11:44:24Z&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2025-09-01&lt;/td&gt;&lt;td&gt;01&lt;/td&gt;&lt;td&gt;Inbound&lt;/td&gt;&lt;td&gt;68099572&lt;/td&gt;&lt;td&gt;110&lt;/td&gt;&lt;td&gt;1900-01-01T12:40:00Z&lt;/td&gt;&lt;td&gt;1900-01-01T13:01:02Z&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2025-09-01&lt;/td&gt;&lt;td&gt;01&lt;/td&gt;&lt;td&gt;Inbound&lt;/td&gt;&lt;td&gt;68099573&lt;/td&gt;&lt;td&gt;110&lt;/td&gt;&lt;td&gt;1900-01-01T22:06:00Z&lt;/td&gt;&lt;td&gt;1900-01-01T22:54:09Z&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2025-09-01&lt;/td&gt;&lt;td&gt;01&lt;/td&gt;&lt;td&gt;Inbound&lt;/td&gt;&lt;td&gt;68099575&lt;/td&gt;&lt;td&gt;110&lt;/td&gt;&lt;td&gt;1900-01-01T23:42:00Z&lt;/td&gt;&lt;td&gt;1900-01-02T00:18:43Z&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;And finally, we need to filter by trips happening after our chosen start time, so we need to handle the problem of dates.&lt;/p&gt;
&lt;h3 id=&quot;dates-and-times-nonsense&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#dates-and-times-nonsense&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Dates and times nonsense&lt;/h3&gt;
&lt;p&gt;How do we assemble the &lt;code&gt;service_date&lt;/code&gt; and &lt;code&gt;scheduled&lt;/code&gt;/&lt;code&gt;actual&lt;/code&gt; fields into an actual timestamp like we got with the subway data?&lt;/p&gt;
&lt;p&gt;Let’s start by parsing the service date.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;timestamp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fromisoformat&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2025-09-01&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2025&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, let’s parse the timestamp offset. Note that it is in UTC, not local time.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;offset &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fromisoformat&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1900-01-01T11:31:00Z&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1900&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tzinfo&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timezone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;utc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Right now, the offset is an &lt;em&gt;aware&lt;/em&gt; datetime, meaning it contains timezone info. Combining aware datetimes with their counterparts, &lt;em&gt;naive&lt;/em&gt; datetimes, is usually not allowed. Let’s make the service date an aware datetime as well.&lt;/p&gt;
&lt;p&gt;You might be tempted to use the &lt;code&gt;astimezone&lt;/code&gt; method, but this will convert the naive datetime to an aware datetime assuming the naive datetime is in local time, which is not what we want — we want to keep the year/month/day values the same, but just attach timezone information to this instance. We can use the &lt;code&gt;replace&lt;/code&gt; method and replace the &lt;code&gt;tzinfo&lt;/code&gt; field.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;timestamp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; timestamp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;replace&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tzinfo&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UTC&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2025&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tzinfo&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timezone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;utc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cool. Now the only thing we need to do with the offset is subtract the placeholder date (1900-01-01). Python implements this nicely, where subtracting one datetime from another gives a &lt;em&gt;timedelta&lt;/em&gt; object.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;offset &lt;span class=&quot;token operator&quot;&gt;-=&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1900&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tzinfo&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UTC&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timedelta&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;seconds&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;41460&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can add this to our service date:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;timestamp &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; offset
&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2025&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tzinfo&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timezone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;utc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, convert it from UTC to our local time, then strip the timezone info to match the behavior of our other code. If this were production code, we would want to only use aware datetimes… but we’re just messing around so let’s do what’s easy.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; zoneinfo
timestamp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; timestamp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;astimezone&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;zoneinfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ZoneInfo&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;America/New_York&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
timestamp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; timestamp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;replace&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tzinfo&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2025&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cool! So that trip was at 2025-09-01 at 7:31 AM.&lt;/p&gt;
&lt;h3 id=&quot;pulling-it-together&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#pulling-it-together&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Pulling it together&lt;/h3&gt;
&lt;p&gt;Okay, back to the show. We were trying to filter by trips happening after a given time. Let’s add a column to our dataframe with a proper timestamp to match our other data.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; zoneinfo

&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;convert_timestamp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;row&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;row&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;actual&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; pd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NA

    timestamp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fromisoformat&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;row&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;service_date&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    offset &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fromisoformat&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;row&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;actual&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    timestamp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; timestamp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;replace&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tzinfo&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UTC&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    offset &lt;span class=&quot;token operator&quot;&gt;-=&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1900&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tzinfo&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UTC&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    timestamp &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; offset
    timestamp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; timestamp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;astimezone&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;zoneinfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ZoneInfo&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;America/New_York&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    timestamp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; timestamp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;replace&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tzinfo&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; timestamp

df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;timestamp&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; df&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;convert_timestamp&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; axis&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After rebuilding &lt;code&gt;trips_from_start&lt;/code&gt; with this new column, we can select the trips after our chosen time, choose the one that departed earliest, and see when it got to Hynes in a similar way to our subway trip simulator.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;timestamp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fromisoformat&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2025-09-20 11:00:00&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
trips_after_time &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; trips_from_start&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;trips_from_start&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;timestamp&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; timestamp&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
next_bus &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; trips_after_time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;loc&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;trips_after_time&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;timestamp&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;idxmin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
bus_arrival &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;half_trip_id&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; next_bus&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;half_trip_id&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;df&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;stop_id&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;79&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Service Date&lt;/th&gt;&lt;th&gt;Route ID&lt;/th&gt;&lt;th&gt;Direction ID&lt;/th&gt;&lt;th&gt;Half Trip ID&lt;/th&gt;&lt;th&gt;Stop ID&lt;/th&gt;&lt;th&gt;Timestamp&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;2025-09-20&lt;/td&gt;&lt;td&gt;01&lt;/td&gt;&lt;td&gt;Inbound&lt;/td&gt;&lt;td&gt;68311684&lt;/td&gt;&lt;td&gt;79&lt;/td&gt;&lt;td&gt;2025-09-20 11:24:05&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;I would’ve made it to Hynes at 11:24 AM. Not bad!&lt;/p&gt;
&lt;h1 id=&quot;simulating-longer-journeys&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#simulating-longer-journeys&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Simulating longer journeys&lt;/h1&gt;
&lt;p&gt;Why do we care about simulating historical journeys, you might ask? It’s useful for answering a few different types of questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How early should I leave my house to make it to work on time 90% of the time?&lt;/li&gt;
&lt;li&gt;How reliably, on average, can I make certain connections between modes?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And, my favorite:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If I wanted to visit every subway station in the MBTA as quickly as possible, what would be the fastest route?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;simulating-an-existing-speedrun&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#simulating-an-existing-speedrun&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Simulating an existing speedrun&lt;/h2&gt;
&lt;p&gt;Transit system speedrunning is a phenomenon in which competitors attempt to travel through all stations within a system as quickly as possible.&lt;/p&gt;
&lt;p&gt;My friend &lt;a href=&quot;https://tris.fyi&quot;&gt;Tris&lt;/a&gt; completed a speedrun about a year ago and documented her route in a &lt;a href=&quot;https://tacobelllabs.net/@tris/113724679621920495&quot;&gt;Mastodon thread&lt;/a&gt;. Let’s see if we can accurately simulate it!&lt;/p&gt;
&lt;p&gt;I made a few improvements to the algorithm:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Caching the processed data where possible!&lt;/li&gt;
&lt;li&gt;Instead of using &lt;code&gt;stop_timestamp&lt;/code&gt; + &lt;code&gt;dwell_time_seconds&lt;/code&gt;, I used the &lt;code&gt;move_timestamp&lt;/code&gt; of the station immediately after the given one. This seemed much more accurate for terminus stations where &lt;code&gt;dwell_time_seconds&lt;/code&gt; was set to null (despite those stations being where the train dwells for the longest amount of time).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For parts of the run involving walking, I set very optimistic transfer times because I can vouch for the fact that Tris walks very fast.&lt;/p&gt;
&lt;p&gt;The code for this is &lt;a href=&quot;https://github.com/breqdev/mbta-simulate&quot;&gt;on GitHub&lt;/a&gt; if you want to play around with it.&lt;/p&gt;



























































































































































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Station&lt;/th&gt;&lt;th&gt;Actual Time&lt;/th&gt;&lt;th&gt;Route&lt;/th&gt;&lt;th&gt;Direction&lt;/th&gt;&lt;th&gt;Simulated Time&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Riverside&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113724740717277822&quot;&gt;07:15&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Green-D&lt;/td&gt;&lt;td&gt;East&lt;/td&gt;&lt;td&gt;07:15&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Kenmore&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Green-B&lt;/td&gt;&lt;td&gt;West&lt;/td&gt;&lt;td&gt;07:48&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Boston College&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113724981377517592&quot;&gt;08:18&lt;/a&gt;&lt;/td&gt;&lt;td&gt;WALK&lt;/td&gt;&lt;td&gt;WALK&lt;/td&gt;&lt;td&gt;08:15&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cleveland Circle&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113725072297288138&quot;&gt;08:41&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Green-C&lt;/td&gt;&lt;td&gt;East&lt;/td&gt;&lt;td&gt;08:25&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Park St&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113725192574260269&quot;&gt;09:12&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Green-E&lt;/td&gt;&lt;td&gt;West&lt;/td&gt;&lt;td&gt;09:07&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Heath St&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113725324958185355&quot;&gt;09:45&lt;/a&gt;&lt;/td&gt;&lt;td&gt;39&lt;/td&gt;&lt;td&gt;Outbound&lt;/td&gt;&lt;td&gt;09:42&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Forest Hills&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113725376738178438&quot;&gt;09:58&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Orange&lt;/td&gt;&lt;td&gt;North&lt;/td&gt;&lt;td&gt;09:54&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Downtown Crossing&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113725464921047879&quot;&gt;10:21&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Red&lt;/td&gt;&lt;td&gt;North&lt;/td&gt;&lt;td&gt;10:17&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Alewife&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113725578546237980&quot;&gt;10:50&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Red&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;10:43&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Davis&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113725596752822002&quot;&gt;10:54&lt;/a&gt;&lt;/td&gt;&lt;td&gt;96&lt;/td&gt;&lt;td&gt;Outbound&lt;/td&gt;&lt;td&gt;10:46&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Medford/Tufts&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113725633977529438&quot;&gt;11:04&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Green-E&lt;/td&gt;&lt;td&gt;West&lt;/td&gt;&lt;td&gt;11:07&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;East Somerville&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;WALK&lt;/td&gt;&lt;td&gt;WALK&lt;/td&gt;&lt;td&gt;11:16&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Union Sq&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113725713807971548&quot;&gt;11:24&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Green-D&lt;/td&gt;&lt;td&gt;West&lt;/td&gt;&lt;td&gt;11:21&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;North Sta&lt;/td&gt;&lt;td&gt;11:36&lt;/td&gt;&lt;td&gt;Orange&lt;/td&gt;&lt;td&gt;North&lt;/td&gt;&lt;td&gt;11:36&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Oak Grove&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113725875079532776&quot;&gt;12:05&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Orange&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;12:03&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Haymarket&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;WALK&lt;/td&gt;&lt;td&gt;WALK&lt;/td&gt;&lt;td&gt;12:23&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Bowdoin&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113725990981012681&quot;&gt;12:35&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Blue&lt;/td&gt;&lt;td&gt;North&lt;/td&gt;&lt;td&gt;12:28&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Wonderland&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113726079382870810&quot;&gt;12:57&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Blue&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;12:50&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;State&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;WALK&lt;/td&gt;&lt;td&gt;WALK&lt;/td&gt;&lt;td&gt;13:08&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Downtown Crossing&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113726156172913175&quot;&gt;13:17&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Red-A&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;13:13&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ashmont&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113726251169030970&quot;&gt;13:41&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Mattapan&lt;/td&gt;&lt;td&gt;Outbound&lt;/td&gt;&lt;td&gt;13:38&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Mattapan&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113726315814984961&quot;&gt;13:57&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Mattapan&lt;/td&gt;&lt;td&gt;Inbound&lt;/td&gt;&lt;td&gt;13:51&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ashmont&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113726356806389810&quot;&gt;14:08&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Red-A&lt;/td&gt;&lt;td&gt;North&lt;/td&gt;&lt;td&gt;14:05&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;JFK/UMass&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113726417955542565&quot;&gt;14:23&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Red-B&lt;/td&gt;&lt;td&gt;South&lt;/td&gt;&lt;td&gt;14:20&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Braintree&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://tacobelllabs.net/@tris/113726512385880967&quot;&gt;14:47&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;14:45&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Not too bad! Especially considering that the “actual time” column is based on Mastodon post timestamps so probably 1-2 minutes behind the actual timing.&lt;/p&gt;
&lt;h2 id=&quot;generating-the-optimal-route&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#generating-the-optimal-route&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Generating the optimal route&lt;/h2&gt;
&lt;p&gt;My eventual plan with this is to use it to learn more about what makes an ideal MBTA speedrun, including the route, timing, etc. It’s been a bucket list item of mine for a while to do a speedrun, and I want to see if I can find something new in terms of route, timing, etc. I think it’s unlikely I’ll find anything that substantial that could make a difference, but maybe it’s possible…&lt;/p&gt;
&lt;p&gt;Since running this type of simulation becomes extremely fast, there are a lot of places you could take this other than just tracing manually entered routes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Taking a graph representation of the MBTA system, running a search algorithm to identify a large set of possible routes, and running simulations on each to estimate real-world timing&lt;/li&gt;
&lt;li&gt;Simulating a route at various times of day, days of the week, or even months of the year to find the best time to start a run&lt;/li&gt;
&lt;li&gt;Implementing logic to simulate making decisions at each step of the route based on upcoming train times (e.g., whether an Ashmont or Braintree train arrives first), to evaluate when to make those decisions&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;takeaways&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#takeaways&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Takeaways&lt;/h1&gt;
&lt;p&gt;I hadn’t really done an in-depth data analysis project quite like this before. I definitely learned a ton!&lt;/p&gt;
&lt;p&gt;Ingesting data into a data structure like this is something I usually consider “grunt work,” but working on this showed me how wrong that assumption can be. I tried to throw a lot of this work to an LLM, only to watch it struggle against the date and timezone issues and completely miss the nuance between &lt;code&gt;stop_timestamp&lt;/code&gt; and &lt;code&gt;move_timestamp&lt;/code&gt;. While I got much further than it did (thankfully for the sake of my job security), it still required me to step away from the problem for a day before I could nail the accuracy.&lt;/p&gt;
&lt;p&gt;I had also heard people constantly talk about how timezones are hard and date parsing is a mess, but had been spared the brunt of that struggle until now. I have discovered that there are a lot of bad ways to represent dates and times in software. I feel lucky that I do not need to deal with things like this more often.&lt;/p&gt;
&lt;p&gt;The last thing I’ll mention is that this project highlighted the difference between working with data in a domain where a dominant format exists (i.e., realtime GTFS), and working in a domain where implementors have no common format to use (i.e., historical transit data). I find it easy to get annoyed at format specifications not perfectly matching what I want to do… but when standardized formats work, they’re pretty great!&lt;/p&gt;</content:encoded></item><item><title>PCBs for LED Matrices</title><link>https://breq.dev/projects/matrix3/</link><guid isPermaLink="true">https://breq.dev/projects/matrix3/</guid><description>Designing a board to make my LED matrix project more compact and versatile</description><pubDate>Sun, 02 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3059&quot; height=&quot;3053&quot; src=&quot;https://breq.dev/_astro/small-weather.m4U5xxPT_Z15Eqwo.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3072&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/large-transit.Vh3WiHCF_24sBgJ.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;I’ve built several projects based on LED matrices: my first one in &lt;a href=&quot;https://breq.dev/projects/wallmatrix&quot;&gt;2021&lt;/a&gt;, and a redesign earlier in &lt;a href=&quot;https://breq.dev/projects/matrix2&quot;&gt;2025&lt;/a&gt; with a larger panel and slimmer enclosure. I’ve also gotten to spend lots of time fine-tuning the display UI as I learn more about icon design, balancing readability and “glanceability” with information density, and dealing with the unique constraints of a display with a 3mm pixel pitch.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;While the hardware was a dramatic step up from my first iteration, I still thought there was room for improvement: more robust mounting of the encoder and the DC power jack, a slimmer construction that could fit closer to the wall, and simpler internal wiring. The obvious next step was a custom PCB. This project came at a time when I was hoping to get back into PCB design, so this seemed like the perfect opportunity!&lt;/p&gt;
&lt;p&gt;At the same time, I had relatively recently started full-time work and wanted to add some decorations to my desk area. Since my previous builds had proven extremely useful at indicating bus times and bike availability, I figured that one on my work desk would be similarly useful! Of course, this one had slightly different requirements, so I ended up building a single board and two very different enclosures for it.&lt;/p&gt;
&lt;h1 id=&quot;pcb-design&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#pcb-design&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;PCB Design&lt;/h1&gt;
&lt;p&gt;The schematic of the board is very similar to that of the previous build, with a few changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;https://www.adafruit.com/product/3211&quot;&gt;Adafruit LED Matrix Bonnet&lt;/a&gt; was removed and its level shifting chips were placed directly onto the custom PCB&lt;/li&gt;
&lt;li&gt;An ADC and photoresistor were added to enable automatic brightness dimming (a substantial pain point on the previous version!)&lt;/li&gt;
&lt;li&gt;A USB-C port was added to the side of the board as an alternate power option, since using the DC jack on the bottom would prevent standing the display up on a table&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;bg-white&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1123&quot; height=&quot;794&quot; src=&quot;https://breq.dev/_astro/schematic.Zsu3y5a8_Z1C7Ygr.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The layout followed pretty naturally. I kept the board at 192mm wide to match either a 64-column 3mm-pitch display or a 32-column 6mm-pitch display, gave it rounded bottom corners to match the style of the previous build, and mounted the encoder at the center on one side.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1123&quot; height=&quot;794&quot; src=&quot;https://breq.dev/_astro/back.1nU-jQCY_Z1C7Ygr.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Shameless plug: The renders in this section were made in &lt;a href=&quot;https://kicad-pretty.breq.dev/&quot;&gt;my KiCAD SVG prettifier tool&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While I like the enclosed build in my apartment, I wanted to leave the option of doing a build with an exposed PCB. The components are all mounted towards the back, and the routing was mostly done only on the back side, leaving the front part of the board mostly empty on both the silkscreen and copper layers. I thought about putting some artwork or text on the silkscreen but couldn’t think of a good way to make it look professional — the usable space on the board is asymmetrical and interrupted by through-hole components.&lt;/p&gt;
&lt;p&gt;In the end, I decided to put a repeating pattern in the copper layer of the board, then order it with &lt;a href=&quot;https://oshpark.com/&quot;&gt;OSHPark&lt;/a&gt;’s &lt;a href=&quot;https://docs.oshpark.com/services/afterdark/&quot;&gt;After Dark&lt;/a&gt; colorway, which uses transparent soldermask to let the copper design shine through. I wanted an angular style to match the vibes of the rigid exposed pixel grid, but worried that an actual grid pattern could clash or appear misaligned. Thus, I chose a hexagonal pattern.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1123&quot; height=&quot;794&quot; src=&quot;https://breq.dev/_astro/front.BMfIQ59A_Z1C7Ygr.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Building and importing this pattern turned into more of an adventure than I had predicted! The process I found is convoluted and by no means ideal. If this were something I was doing more often I would definitely design an automated tool for it, but as it stands, the best way I know to accomplish this is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make your board in KiCAD.&lt;/li&gt;
&lt;li&gt;Draw a shape in Inkscape representing your edge cuts in KiCAD. Make sure the units match up!&lt;/li&gt;
&lt;li&gt;Under “Fill”, choose a pattern you like and set the scaling appropriately.&lt;/li&gt;
&lt;li&gt;Export your KiCAD board as an SVG so you can use it as a reference. The menu option for this is Plot &gt; SVG, then select F.Cu, then check to have Edge.Cuts plotted on all layers.&lt;/li&gt;
&lt;li&gt;Import your plotted SVG into Inkscape and line it up using your Edge.Cuts layer as a reference.&lt;/li&gt;
&lt;li&gt;Manually draw shapes on top of the pattern to mask off all of the areas you do &lt;em&gt;not&lt;/em&gt; want to have your copper pattern. It is easiest to put these in a group. These can be as convoluted or basic as you like — I mostly sketched rectangles over large components and traces.&lt;/li&gt;
&lt;li&gt;Select all of your mask shapes and your shape with the pattern and open the “Shape Builder” tool. Select the area(s) where you want the pattern to show (i.e., the areas without parts). You should be left with a shape matching your intended board, containing your pattern as a fill.&lt;/li&gt;
&lt;li&gt;Next you need to turn the pattern into a basic SVG path. The easiest way to do this is to export it as a PNG (make sure your DPI is 500ish), then re-import it and use the “Trace Bitmap” feature. Click “Update Preview”, and if it looks good then “Apply.”&lt;/li&gt;
&lt;li&gt;Export as yet another SVG. (Make your F.Cu traces hidden before exporting!)&lt;/li&gt;
&lt;li&gt;In KiCAD, Use File &gt; Import &gt; Graphics, select your layer (in our case F.Cu), then place it and manually line it up with your board!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At my girlfriend &lt;a href=&quot;https://miakizz.quest/&quot;&gt;Mia&lt;/a&gt;’s suggestion, I added a photoresistor and ADC to the circuit to implement automatic brightness control. I haven’t gotten around to using this yet, mainly because the build at my work desk does not require it and the enclosure design of the one at home blocks outside light. In the future I think I could add a small hole to the bottom of the design which would let light into the enclosure but not be too visible.&lt;/p&gt;
&lt;h1 id=&quot;pcb-assembly&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#pcb-assembly&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;PCB Assembly&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/smt-level-shifter.okCGhns__ZiaUAA.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;This was my first time doing surface-mount soldering! I kept the components manageable (SOIC chips and 0805 passives). Overall I found the experience much easier than I anticipated. Big thanks to Mia for giving very helpful advice throughout. Thankfully, the board worked on the first try!&lt;/p&gt;
&lt;h1 id=&quot;enclosure&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#enclosure&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Enclosure&lt;/h1&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/small-build-front.CNi5eI7-_25ojXt.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/small-build-back.CZjowQgR_ZNAPfH.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;For the build sitting at my work desk, I wanted to build something that showed off the internals while still appearing sturdy and thoughtfully designed. I went with two small 3D-printed parts on opposite sides of the assembly which attach to both the PCB and the panel, allowing the device to be freestanding on a desk or table.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3072&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/large-bikes.C6Z06A1o_Z1pWE1a.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;For the one in my apartment, I wanted to continue the theme of building something as unobtrusive as possible. Since the PCB was now the same width of the panel, if I wanted to enclose the PCB fully, the enclosure would also need to be a few millimeters wider than the panel. I took this opportunity to recess the panel into the enclosure, giving the system a much more polished look from the side view.&lt;/p&gt;
&lt;p&gt;I am not super happy with the large gaps in the design caused by the imprecision of my 3D printer. I do like that 3D printing can give a brightly colored end result, and the form of the parts seems to lend itself well to a 3D-printed design over something like CNC milling. Perhaps I just need to get access to a better 3D printer.&lt;/p&gt;
&lt;p&gt;The knob was given to me by a coworker who happened to be getting rid of it. It fits my encoder shaft perfectly! It is a bit taller than I would like but that is mostly a function of how much the shaft protrudes. In the future I will get a shorter encoder shaft or find a way to mount the encoder farther back. I unfortunately can’t move the entire PCB back without increasing the thickness of the system because of the height of the 2x8 connector going to the LED panel.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3072&quot; height=&quot;4096&quot; src=&quot;https://breq.dev/_astro/thickness.yv4oNnW7_2i8K8d.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;One thing I do love about the new design is that it sits dramatically closer to the wall, making it match the thickness of photos and artwork I have. Things have come a long way from the ~50mm thickness of the &lt;a href=&quot;https://breq.dev/projects/wallmatrix&quot;&gt;original version&lt;/a&gt;!&lt;/p&gt;
&lt;h1 id=&quot;software&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#software&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Software&lt;/h1&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-3 gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2717&quot; height=&quot;2727&quot; src=&quot;https://breq.dev/_astro/small-bikes.Bt8f3bkn_kJQ8W.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3072&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/large-weather.iTV-N3YC_SMihD.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2466&quot; height=&quot;2466&quot; src=&quot;https://breq.dev/_astro/small-transit.YX120b9d_Z9nhTv.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The largest changes on the software standpoint were extending the previous version to support multiple display sizes and moving a lot of the hardcoded logic (ZIP codes, etc) into a configuration file so my display at work could show different information to my display at home.&lt;/p&gt;
&lt;p&gt;I hacked together a barely-working implementation to test the panel which &lt;a href=&quot;https://avasilver.dev/&quot;&gt;Ava&lt;/a&gt; kindly refactored into something much nicer.&lt;/p&gt;
&lt;h1 id=&quot;conclusion&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;I think time has shown that I’m unlikely to stop building this kind of display anytime soon. They serve a really interesting niche of a device that provides basic information at a glance without being obtrusive, and have a unique and interesting look.&lt;/p&gt;
&lt;p&gt;I really enjoyed getting back into PCB design and finally conquering my fears regarding surface-mount work. I hadn’t actually designed and gotten a board fabricated since I was in high school, more than 5 years ago! It was nice to exercise that skill again. SMT soldering, on the other hand, is something I had never attempted before, being scared off by my general clumsiness and the precision and tools often recommended for that kind of work. While I am very glad I bought flux at the recommendation of a friend, I found it to be smooth sailing even without new tips for my iron. Perhaps I’ll invest in some better tools in the future.&lt;/p&gt;
&lt;p&gt;I loved getting to design this iteration with a focus on making it easy to potentially build more in the future. The board came out quite nice, and while there are definitely some things I would change in the future (mostly relating to making the connectors protrude farther out of the case), I am overall quite happy with the results!&lt;/p&gt;
&lt;p&gt;Throughout this process, several few people have told me that they would love to build something similar to this for their home. I’ve considered a few times trying to scale up production, but the parts cost (mostly the panel itself and the Raspberry Pi) makes the total cost of each unit pretty high. There are a few companies making something similar (&lt;a href=&quot;https://tidbyt.com/&quot;&gt;Tidbyt&lt;/a&gt; and &lt;a href=&quot;https://rideontime.nyc/&quot;&gt;RideOnTime&lt;/a&gt; are the two I’ve seen), but they both use small panels in a thick enclosure style like I did in the &lt;a href=&quot;https://breq.dev/projects/wallmatrix&quot;&gt;first iteration&lt;/a&gt; of this project. I’ve grown to really appreciate the large 64x64 panel size as an option, a flat enclosure, and a design more focused on information density.&lt;/p&gt;
&lt;p&gt;I don’t want to start a business selling these or anything, but I could see myself creating better documentation of what hardware to buy, making the software easier to install and use, and organizing a group buy of boards and parts. Shoot me a message if that sounds interesting to you!&lt;/p&gt;</content:encoded></item><item><title>FDM 3D printing for the home</title><link>https://breq.dev/2025/10/03/fdm/</link><guid isPermaLink="true">https://breq.dev/2025/10/03/fdm/</guid><description>Reflections on ten years of owning a 3D printer.</description><pubDate>Fri, 03 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve owned a 3D printer for about ten years now. I got my first 3D printer, a Monoprice MP Select Mini, when I was in middle school as the 3D printing craze was beginning to take off in “maker” culture. My dad likened it to buying his first computer as a kid. But while the home computer was on the verge of an explosion in popularity, the home 3D printer is still uncommon. The machines have undoubtedly gotten better, but FDM (fused deposition modeling) 3D printers still struggle to find broad appeal beyond the same group of tinkerers who were using them years ago.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Disclaimer: My &lt;a href=&quot;https://www.quadratic3d.com/&quot;&gt;employer&lt;/a&gt; works in the field of 3D printing, but that is neither FDM nor for personal use!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;my-journey-with-fdm&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#my-journey-with-fdm&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;My journey with FDM&lt;/h1&gt;
&lt;p&gt;My &lt;a href=&quot;https://www.mpselectmini.com/&quot;&gt;MP Select Mini&lt;/a&gt; held up okay over quite a few years. The hotend was quite prone to clogs and other issues, so I replaced it with a generic upgrade kit one after printing an adapter I found online. Eventually the mainboard started to give up.&lt;/p&gt;
&lt;p&gt;I now own an Ender 3 Pro printer that I impulse-bought at Micro Center a few years ago for $99. It works… okay. I’ve replaced the bed and upgraded to a metal extruder kit. The Z axis still misbehaves a lot, leading occasionally to layers that are too short (causing nozzle drag). I could try replacing the stepper motor, but I don’t feel like paying $25 just to try something that might not be the actual fix.&lt;/p&gt;
&lt;p&gt;Through my work on the Northeastern Mars Rover Team, I’ve gotten to work with slightly better printers from Bambu and Prusa. They’re generally nicer to get working, but they still experience frequent issues and require consistent maintenance. They’re much closer to a tool you’d find in a machine shop than an appliance you’d find in a home.&lt;/p&gt;
&lt;p&gt;I’ve printed plenty of parts over the years, some more useful than others. Owning a 3D printer has enabled plenty of projects that would otherwise be impossible without a more substantial workshop, like my &lt;a href=&quot;https://breq.dev/projects/wallmatrix&quot;&gt;LED wall sign&lt;/a&gt; and its &lt;a href=&quot;https://breq.dev/projects/matrix3&quot;&gt;sequel&lt;/a&gt;, the &lt;a href=&quot;https://breq.dev/projects/itx-case&quot;&gt;custom mini-ITX case I made&lt;/a&gt;, and more. That said, it requires consistent maintenance to keep working, frequent retries slowing down the process, and produces lots of scrap.&lt;/p&gt;
&lt;p&gt;While I’m happy to print models for my friends, few take advantage of this — most people just do not reach for 3D printing when they encounter a problem.&lt;/p&gt;
&lt;h1 id=&quot;stl-websites&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#stl-websites&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;STL websites&lt;/h1&gt;
&lt;p&gt;Broadly speaking, most personal 3D printing use cases can be divided into categories: printing existing STL models from sites like Thingiverse and Printables, and doing CAD work to produce unique parts.&lt;/p&gt;
&lt;p&gt;The culture of sharing STLs within the hobbyist 3D printing community is huge. It’s trivially easy to download a model from a website, drag and drop it into your slicer, and export G-Code to an SD card for your printer. However, in my experience, most of the models available on these sites are not particularly useful. The vast majority are either decorative parts, or utilitarian parts like headphone stands, shelving and organization products, and cable management parts.&lt;/p&gt;
&lt;p&gt;In most cases, commercially bought parts are available at similar cost and with dramatically better build quality, surface finish, and durability. In some cases it is even faster to go to a physical store to buy a product than to run an FDM printer for hours.&lt;/p&gt;
&lt;p&gt;I can’t speak as much to printing artistic models or figurines, since that aspect never particularly appealed to me. Without postprocessing, FDM 3D prints just don’t look that great.&lt;/p&gt;
&lt;p&gt;There is undoubtedly a sweet spot here: items that are popular enough that you’re likely to find an existing model, but not popular enough to warrant that product being commercially produced. Mounting brackets for specific network switches and similar parts sometimes fall into this category. But the bulk of the time I’ve searched for such a part, 3D models haven’t been available at the usual places, and I’ve had to make it myself.&lt;/p&gt;
&lt;h1 id=&quot;cad-software&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#cad-software&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;CAD software&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1536&quot; height=&quot;1542&quot; src=&quot;https://breq.dev/_astro/floppy-organizer.CtoljkkI_Z19I80h.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;
&lt;/p&gt;&lt;p&gt;A print I recently designed and made for organizing floppy disks for my &lt;a href=&quot;https://breq.dev/2025/07/01/mavica&quot;&gt;Mavica&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;In my experience, without an understanding of &lt;em&gt;some&lt;/em&gt; form of CAD software, owning a 3D printer is of limited utility. Contrast this with the personal computer: most people who use computers on a daily basis do not know how to program! The typical user can meet most or all of their needs by downloading and utilizing existing software.&lt;/p&gt;
&lt;p&gt;I was lucky to learn CAD software in middle school as part of “Technology and Engineering” class, in the golden age where SketchUp was the teaching software of choice instead of TinkerCAD. SketchUp undoubtedly has limitations (I still don’t know how to &lt;a href=&quot;https://forums.sketchup.com/t/how-do-i-create-a-sphere/52661&quot;&gt;draw a sphere&lt;/a&gt;), and the mechanical engineers I’ve worked with have generally laughed at me for using it. That said, I can reasonably efficiently build things.&lt;/p&gt;
&lt;p&gt;Most people I know do not have the motivation or time to learn CAD software just to design and produce one or two parts every few months.&lt;/p&gt;
&lt;p&gt;CAD takes a while, too! Designing good parts requires thinking about the direction of layer lines, need for supports (or lack thereof), balance between strength and filament usage/time, and more, on top of just constructing a part that fits the desired functionality and looks pleasant.&lt;/p&gt;
&lt;p&gt;Can we make CAD more accessible somehow? The advent of tools like TinkerCAD have certainly helped, but building useful parts within its constraints can be difficult. Maybe generative AI can democratize building shitty 3D models in the same way it democratized building shitty software? The attempts I’ve seen at extending generative AI to CAD haven’t been great.&lt;/p&gt;
&lt;p&gt;The whole promise of 3D printing is customized parts built at much faster timescales and with less equipment than otherwise possible. But the typical home user only has the occasional need for a customized part. I use my printer about once a month, sometimes less, mostly because of the time investment of CAD.&lt;/p&gt;
&lt;h1 id=&quot;fitting-into-the-home&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#fitting-into-the-home&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Fitting into the home&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2048&quot; height=&quot;1248&quot; src=&quot;https://breq.dev/_astro/kallax.BZ72RlOa_Z21Ck6p.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;caption&quot;&gt;
&lt;p&gt;My 3D printer sitting atop an IKEA KALLAX. It is quite big!&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Will 3D printers ever be common home appliances?&lt;/p&gt;
&lt;p&gt;One could argue that the limited utility of them precludes that being a possibility. But the success of &lt;a href=&quot;https://cricut.com/en-us/&quot;&gt;Cricut machines&lt;/a&gt; proves that “maker”-oriented devices for the home can be successful! The use cases for the Cricut are similar to those of an FDM 3D printer, they are about a similar size, and the process of using one is similar. But Cricut machines require far less tuning and setup, making useful things on a Cricut requires much less specialized knowledge, and I would guess that Cricut designs have a higher success rate on average than 3D prints.&lt;/p&gt;
&lt;p&gt;I think the type of community surrounding hobbyist 3D printing also plays a role. 3D printing fans are often willing to tinker with their printers, want the ability to customize them, and want to bring their own slicer and tooling. That’s great for consumer advocacy, as evidenced by the failure of RFID-chipped vendor-locked filament among all but the most obscure printers. But it tends to direct the innovation in the 3D printing space towards chasing ultimate customization and featureset instead of building a cohesive user experience like Cricut has. I recognize that buying cheap hobbyist-oriented printers makes me part of this problem, but ultimately, that’s the type of printer that fits most home users’ budgets.&lt;/p&gt;
&lt;p&gt;I really love owning a 3D printer! And I really wish I got to use it more. I am a huge fan of customizing my living space to fit my life with objects that exactly meet my needs, fit my aesthetic, and work with the other objects in my home. 3D printing allows me to do this, and I think that’s wonderful.&lt;/p&gt;</content:encoded></item><item><title>I bought a Sony Mavica!</title><link>https://breq.dev/2025/07/01/mavica/</link><guid isPermaLink="true">https://breq.dev/2025/07/01/mavica/</guid><description>Having fun with a floppy-disk digital camera.</description><pubDate>Tue, 01 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1548&quot; height=&quot;481&quot; src=&quot;https://breq.dev/_astro/ad.BHD25a6U_Z258543.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I bought a Sony Mavica! And everything about this camera has been such a joy, I can’t help but write about it :)&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Digital Mavica&lt;/em&gt; line of cameras saved images directly onto a floppy disk. The model I own is the &lt;a href=&quot;https://camera-wiki.org/wiki/Sony_Mavica_FD90&quot;&gt;MVC-FD90&lt;/a&gt; which was released in 2000. It captures images at a maximum resolution of 1280×960, but can also shoot at 640×480 to save space on the floppy disk (which can store around 20 photos at that resolution).&lt;/p&gt;
&lt;h1 id=&quot;inspiration&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#inspiration&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Inspiration&lt;/h1&gt;
&lt;p&gt;Earlier this year at &lt;a href=&quot;https://www.anthronewengland.com/&quot;&gt;Anthro New England&lt;/a&gt;, my girlfriend &lt;a href=&quot;https://miakizz.quest/&quot;&gt;Mia&lt;/a&gt; was taking photos on her Mavica and let me try it a bit! It seemed like such a fun and whimsical camera to keep around.&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-[112fr_63fr] gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;768&quot; height=&quot;1024&quot; src=&quot;https://breq.dev/_astro/f0000948.CurXKlqz_Z24TvcE.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 gap-2 h-full justify-between&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;768&quot; src=&quot;https://breq.dev/_astro/f0000379.CT-Li3Zi_Z1qbjjN.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;768&quot; src=&quot;https://breq.dev/_astro/f0000525.QBP13tMn_FGSBl.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1216&quot; height=&quot;912&quot; src=&quot;https://breq.dev/_astro/f0001317.C_UHtq_Z_Z1Vbl3p.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;While I was intrigued by the camera, I was a bit intimidated by the floppy disks — Mia has much more experience with retrocomputing than I do, and I worried that getting disks, a drive, pulling images off the camera, and keeping everything in good shape might be too difficult for me.&lt;/p&gt;
&lt;h1 id=&quot;swapfest-my-beloved&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#swapfest-my-beloved&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Swapfest my beloved&lt;/h1&gt;
&lt;p&gt;It was at the most recent &lt;a href=&quot;https://w1mx.mit.edu/flea-at-mit/&quot;&gt;MIT Swapfest&lt;/a&gt; that I picked up my beloved Mavica. I purchased it from some very persuasive transfems who had a table dedicated exclusively to Mavica stuff. I bought the model they recommended and they helped me get set up with a battery, strap, etc. I figured that, now that I’m getting settled into post-college life, now was a good time for me to pick up something new!&lt;/p&gt;
&lt;p&gt;I’m a big fan of Swapfest: it’s almost more of a community gathering to me than a place to buy stuff. Whenever I go I see so many friends, former classmates, and colleagues I know from work. You get to know the tables that come back every month and the people who sell regularly. All in all, it’s a great way to spend a Sunday morning!&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-[63fr_112fr] gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;768&quot; height=&quot;1024&quot; src=&quot;https://breq.dev/_astro/MVC-004F.BxHYl3Fo_2dcP8J.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1024&quot; height=&quot;768&quot; src=&quot;https://breq.dev/_astro/MVC-007F.BjtH0yUa_ZMl9lH.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h1 id=&quot;meticulous-unscheduled-disassembly&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#meticulous-unscheduled-disassembly&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Meticulous unscheduled disassembly&lt;/h1&gt;
&lt;p&gt;Mavica experts will know that aftermarket batteries tend to be a bit oversized compared to the originals. The folks who sold me mine made sure I was aware of this and even gave me a ribbon to put around the battery. However, in my excitement to try it out, I totally forgot about this and immediately put the battery directly in. I had to disassemble the camera to get it out!&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-2 gap-2 max-w-3xl mx-auto&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/folded_open.BdXitOxP_yP8ts.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/under_drive.DCEEL_h5_Z179eNR.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Big thanks to Sony for putting that plastic cover over the flash capacitor… that could’ve been a nasty shock otherwise 😬&lt;/p&gt;
&lt;h1 id=&quot;my-first-disk&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#my-first-disk&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;My first disk&lt;/h1&gt;
&lt;p&gt;A few tables at Swapfest hand out free floppy disks to passerby. I had accumulated a total of three disks and found that, miraculously, one of them actually worked! I had a mild panic moment when I realized the camera couldn’t always successfully read all of the photos on the disk, it turned out to be easily readable by a USB drive.&lt;/p&gt;
&lt;p&gt;At the time, I didn’t have any way to read files off of the disk, so I just took as many as I could until it filled up.&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-[112fr_63fr] gap-2 max-w-3xl mx-auto my-2&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;640&quot; height=&quot;480&quot; src=&quot;https://breq.dev/_astro/MVC-009S.D6EXET8d_9M1rH.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;480&quot; height=&quot;640&quot; src=&quot;https://breq.dev/_astro/MVC-010S.CNfPaK6F_2pDLQY.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-[63fr_112fr] gap-2 max-w-3xl mx-auto my-2&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;480&quot; height=&quot;640&quot; src=&quot;https://breq.dev/_astro/MVC-012S.4ZkcrRKk_vWxCQ.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;640&quot; height=&quot;480&quot; src=&quot;https://breq.dev/_astro/MVC-011S.AlWoyBay_1uQwsS.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-[112fr_63fr] gap-2 max-w-3xl mx-auto my-2&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;640&quot; height=&quot;480&quot; src=&quot;https://breq.dev/_astro/MVC-016S.CDnEGylv_Z1o1i2u.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;480&quot; height=&quot;640&quot; src=&quot;https://breq.dev/_astro/MVC-015S.CE1hhGTO_Z1rFXrl.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-[63fr_112fr] gap-2 max-w-3xl mx-auto my-2&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;480&quot; height=&quot;640&quot; src=&quot;https://breq.dev/_astro/MVC-023S.BkIyhxNV_ZdBJTN.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;640&quot; height=&quot;480&quot; src=&quot;https://breq.dev/_astro/MVC-018S.Dpar-QmE_Z9iAUs.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-[112fr_63fr] gap-2 max-w-3xl mx-auto my-2&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1280&quot; height=&quot;960&quot; src=&quot;https://breq.dev/_astro/MVC-022F.DN_uRRxV_EEeJN.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;480&quot; height=&quot;640&quot; src=&quot;https://breq.dev/_astro/MVC-024S.CXhLgqpF_Z2qQ7t0.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h1 id=&quot;getting-serious&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#getting-serious&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Getting serious&lt;/h1&gt;
&lt;p&gt;By this time, I had had enough fun with the camera that I figured I should actually get ahold of some disks and a floppy reader!&lt;/p&gt;
&lt;p&gt;Mia advised against me buying a random floppy drive on Amazon, so at the recommendation of my friend &lt;a href=&quot;https://tris.fyi/&quot;&gt;Tris&lt;/a&gt;, I bought a Dell Latitude drive online for about $6. It’s designed to be put into a slot on the laptop, but it actually just has a normal Mini-USB connector. I bought some disks from an Amazon listing that looked legit (it was Maxell brand and had good reviews).&lt;/p&gt;
&lt;p&gt;The disks arrived just in time for me to take them on a trip to New York City!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1280&quot; height=&quot;848&quot; src=&quot;https://breq.dev/_astro/MVC-001F.CfIqYRYv_Z133u5I.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-[9fr_4fr] gap-2 max-w-3xl mx-auto my-2&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1280&quot; height=&quot;848&quot; src=&quot;https://breq.dev/_astro/MVC-002F.DYRSm1Gs_14O4eX.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;848&quot; height=&quot;1280&quot; src=&quot;https://breq.dev/_astro/MVC-002F-2.RYqvXRHK_tIREw.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;grid grid-cols-1 sm:grid-cols-[4fr_9fr] gap-2 max-w-3xl mx-auto my-2&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;848&quot; height=&quot;1280&quot; src=&quot;https://breq.dev/_astro/MVC-003F.D1iHC4TD_2lHAzP.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1280&quot; height=&quot;848&quot; src=&quot;https://breq.dev/_astro/MVC-002F-3.hl3MZfGx_ZyIkLa.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1280&quot; height=&quot;848&quot; src=&quot;https://breq.dev/_astro/MVC-004F.DRVWvdCy_Zkarys.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;data-recovery-adventures&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#data-recovery-adventures&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Data recovery adventures&lt;/h1&gt;
&lt;p&gt;After getting back from NYC and finally being able to read the disks, I discovered that a few of the photos weren’t readable. This wasn’t a big deal (the photos I cared most about were fine), but it definitely taught me to be less careless with floppies. My backpack has dozens of tiny magnets in it and I struggled to find a spot far enough away from all of them!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1280&quot; height=&quot;848&quot; src=&quot;https://breq.dev/_astro/damaged_image.B941AoHv_TXbTE.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Mia recommended I use &lt;a href=&quot;https://www.gnu.org/software/ddrescue/&quot;&gt;ddrescue&lt;/a&gt; to try to recover parts of the images. It ended up not giving me anything more than I got from copying files over, but was interesting to learn. I could definitely see it coming in handy in the future.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ddrescue&lt;/span&gt; /dev/sda hdimage &lt;span class=&quot;token builtin class-name&quot;&gt;mapfile&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; /tmp/hdimage
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mount&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; loop hdimage /tmp/hdimage
&lt;span class=&quot;token function&quot;&gt;cp&lt;/span&gt; /tmp/hdimage/* &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;umount&lt;/span&gt; /tmp/hdimage
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To reformat the disks, I used a tool called &lt;a href=&quot;https://manpages.ubuntu.com/manpages/jammy/man8/ufiformat.8.html&quot;&gt;ufiformat&lt;/a&gt; to re-lay the tracks and then used the Mavica’s built-in disk format tool to recreate the filesystem.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; ufiformat /dev/sda
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;more-to-come&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#more-to-come&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;More to come&lt;/h1&gt;
&lt;p&gt;Thus far I’m really enjoying shooting on the Mavica! Limiting myself to a specific number of shots per disk helps me be more thoughtful than with a modern digital camera, but unlike with film, if I take a bad shot I can just delete the file — I haven’t wasted anything.&lt;/p&gt;
&lt;p&gt;Dealing with floppy disks as a medium is much more fun and less stressful than I anticipated! The disks themselves are cheap, devices for reading and writing them are cheap, software support is quite good even on modern machines, and with a little care they’re easy enough to keep in working order.&lt;/p&gt;
&lt;p&gt;I’ve been in a bit of a lull with photography lately, but I think I might’ve found something to get me back into it!&lt;/p&gt;</content:encoded></item><item><title>My Experience with Gender</title><link>https://breq.dev/2025/06/22/gender/</link><guid isPermaLink="true">https://breq.dev/2025/06/22/gender/</guid><description>A reflection on my ever-shifting sense of self.</description><pubDate>Sun, 22 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve noticed that I can be far too quick to assume that the experience of others matches my own. This is one of the reasons I’ve held off on writing about being transgender fo so long: I figured I would just be telling my transgender audience things they already know. But over the past four years, I’ve realized that the way I experience gender has substantial differences to the way every other trans woman I’ve talked to experiences it, just as every trans woman I know has a unique and deeply personal experience compared to any other. I am to highlight this broad range of experiences when I portray my own.&lt;/p&gt;
&lt;p&gt;The other reason I have held off on discussing my experience is that I often mistake the hesitance of my cisgender friends to ask questions as apathy. While I know many other transgender people are less open about their experience (which is understandable!), I have always been overjoyed to have conversations about gender and my existence as a trans woman with the cis people in my life. I hope this post can lay out the groundwork and serve as an invitation for people I know to start these conversations with me.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;My wonderful girlfriend Mia has also written a blog series about being transgender, which you can find &lt;a href=&quot;https://miakizz.quest/posts/trans1&quot;&gt;here&lt;/a&gt;. You should read that one after this — I always look up to her wisdom when it comes to big topics like this :)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br&gt;
&lt;blockquote&gt;
&lt;p&gt;Update: My girlfriend &lt;a href=&quot;https://avasilver.dev/blog/transition&quot;&gt;Ava&lt;/a&gt; and my friend &lt;a href=&quot;https://tris.fyi/blog/thoughts-about-gender.html&quot;&gt;Tris&lt;/a&gt; have also written very insightful posts about this!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;early-transition&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#early-transition&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Early Transition&lt;/h1&gt;
&lt;p&gt;A commonality between most transgender people I know is that our decision to transition is defined by strong emotions. Deciding to transition is deciding between something very known — your everyday life, as it is now — and something entirely unknown. It is impossible to know beforehand what emotions you will experience after coming out. Nobody can tell you with perfect accuracy if transitioning is something that would make you happier.&lt;/p&gt;
&lt;p&gt;The only way to know for certain is to try it in a truly immersive sense: name, pronouns, and the way you present yourself in every aspect of life. Unfortunately, we live in a society where this is often somewhere on the scale of impractical to impossible. And thus, most trans people are going in blind.&lt;/p&gt;
&lt;p&gt;In my case, the strong emotion that determined my decision to transition was fear. Not fear of stepping into this unknown experience, but fear of &lt;em&gt;not&lt;/em&gt; doing so. I was depressed in the years leading up to the start of my transition (for me, the last two years of high school). Had I not transitioned, I may not have made it to here today. Despite being a complete unknown, transition felt like my best chance of survival.&lt;/p&gt;
&lt;p&gt;While I know my story is far from unique, it brings me happiness that there are those for which this decision is led primarily by positive emotions, too.&lt;/p&gt;
&lt;p&gt;I chose to delay my social transition until starting college since moving cities let me avoid or postpone coming out to people I already knew. While diving headfirst into being “Brooke, she/her” was overwhelming at times, it was better than having to hold a “coming out” discussion with everyone in my life at the time.&lt;/p&gt;
&lt;p&gt;It is a cruel irony that one of the most difficult parts of transition — having to explain to everyone in your life what being transgender is and answer countless questions about something so deeply personal to you — happens so early on in the process, before you have had a chance to build confidence in yourself or even grow to understand yourself. It makes me happy to see communities in which gender fluidity and questioning are more widely accepted and supported.&lt;/p&gt;
&lt;p&gt;For the first year of my transition, I honestly didn’t have the energy to think critically about my own experience of gender. I knew that presenting femininity felt nice, but the details didn’t matter — I was too focused on learning how to buy clothes that I like (and throwing out everything I knew about how clothes were “supposed to” fit), learning how to take care of long hair, and overcoming my fears, one step at a time.&lt;/p&gt;
&lt;p&gt;I take my confidence for granted nowadays, but I still vividly remember my first time leaving my dorm room wearing a skirt. I remember noticing how much my hands were shaking when reaching for the elevator button, and trying to make myself small in any way possible. I am six feet tall, and while I enjoy being loud and confident nowadays, there was a long stretch of time when I just wanted to blend into the background.&lt;/p&gt;
&lt;h1 id=&quot;gaining-confidence&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#gaining-confidence&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Gaining confidence&lt;/h1&gt;
&lt;p&gt;The next year of my transition is when I finally started to feel like I could “take up space” in social gatherings. I dyed my hair bright pink for the first time and started to assert myself more. I finally started to feel okay with using the women’s restroom instead of walking halfway across campus for a gender-neutral one.&lt;/p&gt;
&lt;p&gt;While I made a few false starts at voice training, over time I realized that I had become comfortable with my voice as it is without deliberate training. While I’m less likely to be gendered correctly by strangers in public, I can still feel safe in that my friends, peers, and coworkers will not see it as evidence that I am any less of a woman. While I’m not ruling out voice training in the future, it would be on my terms.&lt;/p&gt;
&lt;p&gt;My goal in transition is not to assimilate into cisnormative society. It is to wake up each morning and present myself to the world in a way that sparks joy.&lt;/p&gt;
&lt;p&gt;Being transgender does not define my life nowadays to the extent it used to a few years ago. On one hand, the day-to-day stresses of my early transition years have subsided, and I am much happier as a result. On the other hand, I do think my &lt;em&gt;trans-ness&lt;/em&gt; — my experience of transitioning, my presence in transgender social circles, and the unique perspective I have as a result — is one of the most notable pieces of my life.&lt;/p&gt;
&lt;p&gt;I would say that in my day to day life, I am a very happy person. I would also say that my trans-ness is an aspect of my life that brings me happiness. This would have been unfathomable to my past self.&lt;/p&gt;
&lt;h1 id=&quot;finding-stability&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#finding-stability&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Finding stability&lt;/h1&gt;
&lt;p&gt;The word “transition” implies that the process of changing one’s gender happens over a well-defined time interval. This is, in my eyes, inaccurate.&lt;/p&gt;
&lt;p&gt;In one sense, my transition “ended” when I made the commitment to myself that I was a woman. The social steps — changing the way I dressed, coming out to friends and family, updating my legal name — were all just logistics.&lt;/p&gt;
&lt;p&gt;In another sense, transition is never “done.” As my body cannot naturally produce estrogen, I will keep taking hormone replacement therapy for as long as I live. The style of clothing I wear will continue to shift throughout my life. There are still people who knew me before transition and who I will need to come out to if we ever cross paths again.&lt;/p&gt;
&lt;p&gt;It is only recently that I have felt stable enough to reconsider some of the fundamental questions of my transition. I enjoy the way I dress, the way people refer to me, and the effects that feminizing HRT brings to my body. But the label of “transgender woman” was less something I decided on and more something that was handed to me as a result of this.&lt;/p&gt;
&lt;p&gt;I’ve begun to question whether “non-binary” describes the way I experience gender. I’ve always felt a bit of dissonance with being referred to as a woman, and for the longest time, I wrote that off as imposter syndrome. But four years in, I’m as confident as can be, and that feeling is still there.&lt;/p&gt;
&lt;p&gt;Perhaps this questioning could only happen this many years in. It’s taken time for “they/them” pronouns to stop feeling like a way for people to avoid acknowledging my transition and gender, and start feeling like them acknowledging the complexities of my gender beyond the binary.&lt;/p&gt;
&lt;p&gt;Alternatively, perhaps this was down to a lack of representation of non-binary people in my social groups. While early in transition, I sought out friends with similar experiences to me, and ended up in groups primarily consisting of binary trans women. At present, several close friends of mine are non-binary, and I’ve been able to have many more interesting conversations about gender with people outside the gender binary. These conversations have undoubtedly been a catalyst for questioning my own gender identity.&lt;/p&gt;
&lt;h1 id=&quot;conclusion&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;There has been an ongoing bit on this website since &lt;a href=&quot;https://github.com/breqdev/breq.dev/commit/d054f38bb5fc75b07d22fcc479dee654eb7de815&quot;&gt;three years ago&lt;/a&gt; in which, at some random probability, the pronouns displayed on the homepage will be either “she/her” or “she/they”. I wish I could remember what was in my head when I wrote that.&lt;/p&gt;
&lt;p&gt;One explanation is that labels have always felt unimportant in relation to my own experience. While I recognize how powerful labels can be in helping people explain their identity and connect to others, I find their imperfect fit frustrating. I care less about whether I am called “she” or “they,” and far more about the motivations of those calling me this. Am I being called “they” in an attempt by the speaker to avoid recognizing or legitimizing my identity as a woman? Or am I being called “they” in recognition of the fact that my identity does not perfectly map onto the gender binary?&lt;/p&gt;
&lt;p&gt;I hope that in five years I can re-read this post and find myself disagreeing with parts of it. I hope that my perception of myself, of transness, and of the way I fit into society will shift over time. I want to continue thinking about, exploring, and experimenting with my own sense of gender identity.&lt;/p&gt;</content:encoded></item><item><title>My Hair Routine</title><link>https://breq.dev/2025/06/21/hair/</link><guid isPermaLink="true">https://breq.dev/2025/06/21/hair/</guid><description>(hint: it&apos;s a lot of detangling work)</description><pubDate>Sat, 21 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have very thick, curly, bleached hair, which is about as “hard mode” as you can get. I have gotten &lt;em&gt;so much&lt;/em&gt; bad advice from people about how to care for it. After a few years and some tips from friends, here’s the system I’ve ended up with!&lt;/p&gt;
&lt;div class=&quot;callout-gray&quot;&gt;
&lt;p&gt;I’ve added products I use in the toggle elements below, since I think providing specific examples can be useful, but I don’t want this to come across as too much of an endorsement. I know enough about this topic to solve problems, but I’m not 100% sure I’ve landed on the best set of products for my hair. You, dear reader, likely have different hair with different needs anyway.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2311&quot; height=&quot;2311&quot; src=&quot;https://breq.dev/_astro/utah.B5wFkrZO_1Jz80o.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;Last year at the University Rover Challenge. I&apos;ve grown out my hair quite a bit since then!&lt;/p&gt;
&lt;h1 id=&quot;requirements&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#requirements&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Requirements&lt;/h1&gt;
&lt;p&gt;To give some context, here are the requirements I have for my hair routine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It must actually solve the tangle problem (this one is very hard)&lt;/li&gt;
&lt;li&gt;Regular maintenance must be a reasonably straightforward process, since I am lazy&lt;/li&gt;
&lt;li&gt;Products should be as easy to get as possible (ideally, I should be able to get everything from a CVS or Target)&lt;/li&gt;
&lt;li&gt;The process should not impede my ability to travel — I should easily be able to pack a small, TSA-legal bag for week-long trips with necessities&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;regular-maintenance&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#regular-maintenance&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Regular maintenance&lt;/h1&gt;
&lt;p&gt;Whenever I shower, there are two main hair-related tasks I focus on. Detangling is the endless struggle I face, and what the majority of my routine is built around. Washing ends up essentially being a by-product of the detangling process, not the other way around.&lt;/p&gt;
&lt;p&gt;I generally almost never use shampoo! My regular process consists of two steps: a washing conditioner (aka “cowash”) and a leave-in conditioner. I first add the washing conditioner at the roots of my hair, scrub it in, let it sit while I wash my body, loosely detangle my hair with my fingers, then rinse it out. Then, I add leave-in conditioner, focusing on the roots of my hair, and use a wide-tooth comb to continue detangling.&lt;/p&gt;
&lt;p&gt;Often when I mention that I don’t use shampoo, the response I hear is “my hair is so oily, I could never,” and this is often valid! I am lucky that oily hair is a problem that I do not face, and I find cowash to help with tangles much more, so it is the solution I use.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Products&lt;/summary&gt;
&lt;p&gt;I use &lt;em&gt;As I Am Coconut Cowash&lt;/em&gt; as my washing conditioner. For leave-in conditioner, I use either &lt;em&gt;As I Am Classic Leave-In Conditioner&lt;/em&gt; or &lt;em&gt;Kinky-Curly Knot Today&lt;/em&gt;. I use a &lt;em&gt;Wet Brush Detangling Comb&lt;/em&gt; with the zig-zag teeth.&lt;/p&gt;
&lt;/details&gt;
&lt;h1 id=&quot;less-frequent-maintenance&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#less-frequent-maintenance&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Less frequent maintenance&lt;/h1&gt;
&lt;p&gt;Very occasionally, I will use shampoo if I need to clean my hair more vigorously than washing conditioner allows. I’ve &lt;em&gt;heard&lt;/em&gt; that sulfate-free types are better for “low-shampoo” routines like mine, since they don’t leave oils as much.&lt;/p&gt;
&lt;p&gt;I do also have a styling cream I can use, but I don’t find myself reaching for it very much.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Products&lt;/summary&gt;
&lt;p&gt;The shampoo I use is &lt;em&gt;Herbal Essences Bio Renew Aloe + Eucalyptus Scalp Balance Shampoo&lt;/em&gt;. The styling cream is &lt;em&gt;Shea Moisture Curl Enhancing Smoothie&lt;/em&gt;.&lt;/p&gt;
&lt;/details&gt;
&lt;h1 id=&quot;coloring&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#coloring&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Coloring&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2510&quot; height=&quot;2850&quot; src=&quot;https://breq.dev/_astro/brown_hair.-emM16bi_ZXEpJr.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;Me in 2021 at the start of college -- I looked pretty different at the time!&lt;/p&gt;
&lt;p&gt;Unfortunately, bright pink is not the natural color of my hair, so I need to dye it every month or so to keep it colorful!&lt;/p&gt;
&lt;p&gt;I dye my hair DIY-style, for two main reasons. The most obvious is that I started off with hair dye as a student — I can afford to get it professionally dyed now, but I couldn’t back then. But more importantly, I view it as a really fun activity to do with people I’m close with. The joy of doing it with someone I know vastly outweighs the somewhat inconsistent results I get :)&lt;/p&gt;
&lt;p&gt;I don’t always use the same shade of dye each time, but the resulting color stays pretty consistent since it blends with the residual color from prior applications.&lt;/p&gt;
&lt;p&gt;Every few dye sessions, I touch-up bleach the roots of my hair. The first initial bleach was a massive ordeal that took three boxes, but since then, I’ve just used one box every few months.&lt;/p&gt;
&lt;p&gt;I also dye my girlfriend &lt;a href=&quot;https://avasilver.dev&quot;&gt;Ava&lt;/a&gt;’s hair with the same setup.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;6000&quot; height=&quot;4000&quot; src=&quot;https://breq.dev/_astro/old_color.DNzSyuk9_2sGY3Q.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;An older shade of dye I previously used. Picture from 2023.&lt;/p&gt;
&lt;p&gt;I use a semi-permanent dye (doesn’t require developer) since it’s much less stressful and easier to apply, and it seems to last as long as the “permanent” ones I’ve used. (I’ve accidentally fallen asleep with hair dye in and been fine!) I used to use a boxed “permanent” dye (and even once went on a 2+ hour bus journey into the a CVS in the Boston suburbs when the chain stopped stopped carrying it just so I could buy up the remaining stock), but in hindsight I didn’t like the color it gave as much.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Products&lt;/summary&gt;
&lt;p&gt;I use Arctic Fox dyes, mostly &lt;em&gt;Virgin Pink&lt;/em&gt; but sometimes &lt;em&gt;Electric Paradise&lt;/em&gt;. I’ve tried &lt;em&gt;Girls Night&lt;/em&gt; and &lt;em&gt;Porange&lt;/em&gt; but haven’t continued to use them. Ava’s hair is &lt;em&gt;Purple AF&lt;/em&gt;. I also use a mixing bowl and brush from Arctic Fox.&lt;/p&gt;
&lt;p&gt;For bleach, I usually buy a generic boxed kind such as &lt;em&gt;Colorista All Over&lt;/em&gt;. I don’t use the included toner since I’ll immediately put color on top of it anyway.&lt;/p&gt;
&lt;p&gt;The original box dye I used was &lt;em&gt;got2b Metallics Sakura Pink&lt;/em&gt;.&lt;/p&gt;
&lt;/details&gt;
&lt;h2 id=&quot;color-conditioner&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#color-conditioner&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Color conditioner&lt;/h2&gt;
&lt;p&gt;A few people I know have recommended creating a color conditioner by combining my conditioner with a few drops of hair dye. I’m interested in this idea to supplement my regular re-dye process, but there are a few reasons I’ve held off:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I worry about color bleed from my hair staining things — currently this is only a problem for the first few washes after a dye session, but if I am constantly reapplying dye, it could cause more problems&lt;/li&gt;
&lt;li&gt;I like being able to loan my hair products to friends who are staying the night, etc., most of whom do not want pink hair&lt;/li&gt;
&lt;li&gt;I’m happy with the way my two types of conditioner work, and I don’t want to mess with the system :)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That said, I will probably still try this at some point once the time is right!&lt;/p&gt;
&lt;h1 id=&quot;travel&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#travel&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Travel&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2159&quot; height=&quot;3473&quot; src=&quot;https://breq.dev/_astro/hotel.dxDuKEAu_1lHSgj.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;Me this summer traveling for the University Rover Challenge.&lt;/p&gt;
&lt;p&gt;I take a lot of overnight or weekend trips, and I like not having to worry about having my favorite hair stuff with me, so I keep my hair products in 3oz travel containers. It’s also quite helpful for fitting all of the containers onto limited shower shelf space, and for dispensing small amounts of cowash (since mine comes in a tub).&lt;/p&gt;
&lt;p&gt;I haven’t found a good solution for labeling these yet, and I’m worried that I’ll eventually mistake two similar-looking products, but so far I’ve been getting by with occasionally re-writing labels on them with sharpie.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Products&lt;/summary&gt;
&lt;p&gt;I just bought a random set of containers from Target, I can’t find the exact one I got anywhere. But the important part is just that it’s 3-4 small bottles with squirt lids. Search “TSA Travel Container Set” or “TSA Travel Bag Kit” on the website and you’ll find similar stuff.&lt;/p&gt;
&lt;/details&gt;
&lt;h1 id=&quot;conclusion&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;This system has worked for me for the past ~4 years! I think it balances my needs well and I’m proud that I’ve found something that works.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2000&quot; height=&quot;1333&quot; src=&quot;https://breq.dev/_astro/lab.BVxKNzG9_Z19ezR5.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;A recent-ish picture of me having fun in the Rover lab, with a slightly oranger shade of dye.&lt;/p&gt;</content:encoded></item><item><title>Tiny Devices I Love</title><link>https://breq.dev/2025/05/02/pocket-size/</link><guid isPermaLink="true">https://breq.dev/2025/05/02/pocket-size/</guid><description>Pocket-sized tools I greatly adore.</description><pubDate>Fri, 02 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve developed a strong appreciation for small devices that solve problems for me. While larger versions of these tools exist, there are a few reasons why these tiny tools hold a special place in my heart.&lt;/p&gt;
&lt;p&gt;The first is that I have spent the last several years living in small dorm rooms and now live in a relatively small apartment. While I wish I had the space for a dedicated electronics workbench, the practical reality is that my tools need to fit either on my normal-sized desk or be stored away in a drawer. The second is that, both for my work with the Northeastern Mars Rover Team and for my own projects, I do occasionally end up needing to fix, debug, or bodge something together in locations which are less conducive to this type of work (such as the middle of a grassy field).&lt;/p&gt;
&lt;p&gt;The criteria I decided on for devices in this category include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The device must be “cute.” More formally, this includes things that are about the size you could fit into a large pocket. It also means that the device must be smaller than most others in its class.&lt;/li&gt;
&lt;li&gt;The device must be something I would carry somewhere to perform a task. While I love my tiny &lt;a href=&quot;https://shop.netgate.com/products/1100-pfsense&quot;&gt;Netgate 1100&lt;/a&gt; (a gift from &lt;a href=&quot;https://adryd.com/&quot;&gt;Ari&lt;/a&gt;), it’s not something I would transport to somewhere for a temporary installation.&lt;/li&gt;
&lt;li&gt;The device must be good at what it does. It must not sacrifice functionality to become its small size.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Acknowledgements:&lt;/strong&gt; This post was loosely inspired by my friend Hunter’s &lt;a href=&quot;https://pixilic.com/devices-of-all-time&quot;&gt;Devices of All Time&lt;/a&gt; post. Many of these devices were gifts from close friends!&lt;/p&gt;
&lt;h1 id=&quot;ifixit-moray-driver-kit&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#ifixit-moray-driver-kit&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;iFixit Moray Driver Kit&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3072&quot; height=&quot;2870&quot; src=&quot;https://breq.dev/_astro/ifixit.D79EJGpr_26dWXM.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Gift from &lt;a href=&quot;https://adryd.com/&quot;&gt;Ari&lt;/a&gt; and &lt;a href=&quot;https://pixilic.com/&quot;&gt;Hunter&lt;/a&gt;!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is a screwdriver kit including a small screwdriver and a large collection of bits. It comes in a hard plastic case, the lid of which doubles as a tray for storing screws.&lt;/p&gt;
&lt;p&gt;I’ve used the Moray’s older sibling, the Mako, extensively on the rover team, but haven’t had my own iFixit kit until recently! I like to flip the bits I’m using for a project down to visually distinguish them from the rest and make them easier to grab.&lt;/p&gt;
&lt;p&gt;Likes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Very small and cute. Easy to throw into a backpack or tool bag.&lt;/li&gt;
&lt;li&gt;Extremely versatile, good selection of bits. Replaces the need to carry a whole range of screwdriver sizes.&lt;/li&gt;
&lt;li&gt;Handle size is large enough for “big screwdriver” tasks, but small enough to allow for delicate work.&lt;/li&gt;
&lt;li&gt;Spots for bits are labeled with both an icon and text.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dislikes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No 2.0mm hex bit. (I encounter M2.5 screws enough that I’m surprised this was omitted!)&lt;/li&gt;
&lt;li&gt;While additional bits are available, there aren’t any additional spots in the case to hold them. (It’s possible to store them within the screwdriver, but that makes them harder to access.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;fnirsi-dps-150-dc-power-supply&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#fnirsi-dps-150-dc-power-supply&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;FNIRSI DPS-150 DC Power Supply&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/power-supply.BIKMb6mW_ZH0eAc.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Gift from &lt;a href=&quot;https://miakizz.quest&quot;&gt;Mia&lt;/a&gt;!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is a benchtop DC power supply, which is an essential tool for electronics prototyping. This type of power supply is useful as it precise voltage control and current monitoring and limiting to protect your circuit. It provides power through “banana plugs” or directly to wires via the two terminals.&lt;/p&gt;
&lt;p&gt;Likes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Taking power from a USB-C port greatly reduces the size of the unit.&lt;/li&gt;
&lt;li&gt;Screen is quite readable and useful for precise adjustments.&lt;/li&gt;
&lt;li&gt;Maximums of 30 volts and 5 amps.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dislikes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Display hinge struggles to hold itself up.&lt;/li&gt;
&lt;li&gt;Voltage/current input is a bit unintuitive.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;glinet-opal-ac1200-router&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#glinet-opal-ac1200-router&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;GL.iNet Opal (AC1200) Router&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/gl-inet.DSXR3TRT_2mAWIN.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/gl-inet-ports.Bq3mBeDL_27FqlL.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;This is a “travel router”: A small, lightweight router and wireless access point powered over USB-C. This one in particular is quite cheap and actually ran my home network for a bit until I was able to set up the pfSense unit I use now. It’s also saved the day at Rover events a few times when I’ve used it to establish a wireless network in areas otherwise slightly too far from a nearby building’s coverage.&lt;/p&gt;
&lt;p&gt;Likes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;USB type-C power input.&lt;/li&gt;
&lt;li&gt;Two LAN Ethernet ports.&lt;/li&gt;
&lt;li&gt;Strong featureset provided by OpenWRT.&lt;/li&gt;
&lt;li&gt;Antennas fold in for compactness when not in use.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dislikes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Runs a very old patched version of OpenWRT, not the mainline version.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;radioshack-22-182-digital-multimeter&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#radioshack-22-182-digital-multimeter&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;RadioShack 22-182 Digital Multimeter&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3072&quot; height=&quot;4096&quot; src=&quot;https://breq.dev/_astro/multimeter.Zzi4bXZR_ZW0Ybt.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This was a gift from… my dad probably? I’ve had it as long as I can remember.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is a multimeter: a device that measures the voltage difference between two points, the current flowing through itself, or the resistance of an electrical component. While some meters support “autoranging,” this one requires that you specify the range of values you expect the quantity you measure to be in (e.g. 200V, 20V, or 2V). I honestly prefer manual ranging meters, since dialing in the voltage at the start is usually easy to do and the meter responds more quickly when it doesn’t need to go through the autoranging sequence.&lt;/p&gt;
&lt;p&gt;Likes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Carrying case keeps probes inside.&lt;/li&gt;
&lt;li&gt;No need to switch probes between sockets for current vs voltage measurement.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dislikes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Current measurements limited to 200mA.&lt;/li&gt;
&lt;li&gt;Case is slightly too small for probes to fit comfortably.&lt;/li&gt;
&lt;li&gt;No beeper for continuity testing.&lt;/li&gt;
&lt;li&gt;Uses an uncommon 12V battery.&lt;/li&gt;
&lt;li&gt;Not as precise as larger meters.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;pinecil-v2&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#pinecil-v2&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Pinecil V2&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/pinecil.BTZaanxr_Z1BkxLt.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Gift from &lt;a href=&quot;https://adryd.com/&quot;&gt;Ari&lt;/a&gt;!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is a soldering iron: a tool that heats its tip up to a high enough temperature to melt solder (metal) and join electrical wires. Other potential uses include pushing heat-set metal inserts into plastic parts. This is a temperature-controlled iron, which is essential to allow heating solder efficiently without damaging components from excessive heat.&lt;/p&gt;
&lt;p&gt;Likes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;USB-C power input.&lt;/li&gt;
&lt;li&gt;Readable monochrome OLED display.&lt;/li&gt;
&lt;li&gt;Indicators for iron cooldown.&lt;/li&gt;
&lt;li&gt;IMU-based standby power-off.&lt;/li&gt;
&lt;li&gt;Easily swappable tips.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dislikes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Doesn’t include a stand or case of any sort.&lt;/li&gt;
&lt;li&gt;Some intermittent issues with connectivity between the iron and tip.&lt;/li&gt;
&lt;li&gt;Two-button based interface is sometimes unintuitive to use.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;aioc-all-in-one-cable&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#aioc-all-in-one-cable&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;AIOC (All-in-One Cable)&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/aioc.DZVx3-n0_Z1uvff1.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is from a run made by &lt;a href=&quot;https://miakizz.quest/&quot;&gt;Mia&lt;/a&gt;, although I believe I paid her back for it? Mia if you’re reading this and I owe you money, please let me know :)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is a device with two purposes: to upload configuration data (“codeplugs”) to handheld radios, and to connect a handheld radio as a soundcard to a computer. The former use case is nice for programming radio channels as it allows configurations to be easily created and distributed. The latter use case enables computer modulation techniques such as AX.25.&lt;/p&gt;
&lt;p&gt;Likes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Smaller than conventional radio programming cables.&lt;/li&gt;
&lt;li&gt;Alignment between 2.5mm and 3.5mm jacks is not sturdy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dislikes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Can’t be used to program my Retevis RT3S.&lt;/li&gt;
&lt;li&gt;3D printed case is flimsy.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;conclusion&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;You’ll notice I didn’t put direct links to where to buy any of these (and some of them, like that Radio Shack multimeter, are likely impossible to get these days). My intent is to show how these tools fit the style of work that I do and enable me in ways traditional alternatives don’t, and to maybe help you consider how the tools you use in your work shape the projects you take on and the methods you use.&lt;/p&gt;</content:encoded></item><item><title>Mini-Rack Homelab</title><link>https://breq.dev/projects/minirack/</link><guid isPermaLink="true">https://breq.dev/projects/minirack/</guid><description>A 10&quot; rack system for network equipment and experimentation.</description><pubDate>Tue, 29 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/rack.DhrY6NZ4_vdbik.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I recently reorganized all of my home networking hardware into a 10” rack that sits in an IKEA KALLAX shelf next to my desk.&lt;/p&gt;
&lt;h1 id=&quot;why-mini-rack&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#why-mini-rack&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Why Mini-Rack?&lt;/h1&gt;
&lt;p&gt;I was motivated to take this project on by Jeff Geerling’s &lt;a href=&quot;https://mini-rack.jeffgeerling.com/&quot;&gt;Project Mini Rack&lt;/a&gt; website, which collects links to hardware and guides for rackmount builds in the 10” form factor. I’ve wanted to build out a rackmount system for ages, partly because I want a rigid frame for my networking hardware (as opposed to a loose collection of boxes sitting atop a shelf), and partly because I love the aesthetics of rackmount gear. While I could undoubtedly achieve the same goals in this project without using a rack at much less cost, I’ve wanted to play with something in the rackmount form factor for &lt;em&gt;years&lt;/em&gt;. I had largely written it off as impractical given the small size of the apartment, but the proliferation of 10” gear has made it possible to set up a compact rackmount homelab system.&lt;/p&gt;
&lt;h1 id=&quot;goals-and-improvements&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#goals-and-improvements&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Goals and Improvements&lt;/h1&gt;
&lt;p&gt;My existing home network setup is designed for a small apartment which I share with my girlfriend &lt;a href=&quot;https://avasilver.dev/&quot;&gt;Ava&lt;/a&gt;. We get internet service from a cable connection (no fiber here unfortunately), and we bought a basic Arris SURFboard modem. From here, we use a Netgate pfSense router gifted by my other girlfriend &lt;a href=&quot;https://adryd.com/&quot;&gt;Ari&lt;/a&gt;, which also runs a site-to-site Wireguard VPN connecting to her home network. The router connects to a Ubiquiti U6 Mesh access point which both provides our personal Wi-Fi network and functions as a working &lt;a href=&quot;https://eduroam.org/&quot;&gt;eduroam&lt;/a&gt; service provider with client isolation (the details of which will remain a mystery).&lt;/p&gt;
&lt;p&gt;The wired side of the network runs through a Netgear GS105 switch which I got at &lt;a href=&quot;https://w1mx.mit.edu/flea-at-mit/&quot;&gt;MIT Swapfest&lt;/a&gt; for five bucks. (My switch was actually, unbeknownst to me, featured on my friend &lt;a href=&quot;https://pixilic.com/devices-of-all-time&quot;&gt;Hunter’s blog&lt;/a&gt; last year.) From here, wired connections run to my desktop and Ava’s desktop.&lt;/p&gt;
&lt;p&gt;My primary goal for this project was to reduce the clutter on my shelf and replace it with something that looked cool. I also wanted to finally have a good way to self-host things again. While I’m happy to keep most services hosted online, it would be nice to have some services running locally where convenient.&lt;/p&gt;
&lt;h1 id=&quot;networking-setup&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#networking-setup&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Networking Setup&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/empty-rack-on-shelf.Bx8NWB9e_2wz5YU.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I chose a &lt;a href=&quot;https://deskpi.com/products/deskpi-rackmate-t1-rackmount-10-inch-4u-server-cabinet-for-network-servers-audio-and-video-equipment&quot;&gt;RackMate T0&lt;/a&gt; case, since it would fit within a single shelf of the IKEA KALLAX next to my desk (and because the next size up was 8U and I doubted I would have enough hardware to justify that). The case arrived with a broken top acrylic panel, but the folks at GeeekPi (DeskPi) shipped me a replacement quickly without any hassle! In the meantime, I got to start building out the rest of the rack.&lt;/p&gt;
&lt;p&gt;No rack is complete without a patch panel, and mine needed some way to pass cables through from the front-mounted Ethernet ports on my router and network switch. I chose a &lt;a href=&quot;https://deskpi.com/products/deskpi-rackmate-accessory-10-inch-network-switch&quot;&gt;12-port model from DeskPi&lt;/a&gt;, designed for the rack I used. This provides more ports than I need for my modem connection, access point, and for our desktops, but the Ethernet ports can be swapped for any keystone block in case I want to use them for any other purpose.&lt;/p&gt;
&lt;p&gt;One problem I wanted to solve with this design was the bundle of power bricks tangled up behind my shelf. I saw the &lt;a href=&quot;https://deskpi.com/products/deskpi-dc-pdu-lite-7-ch-0-5u-for-deskpi-rackmate-t1?_pos=1&amp;#x26;_sid=782266501&amp;#x26;_ss=r&quot;&gt;DeskPi PDU Lite&lt;/a&gt; available, and decided to use it in combination with a 12V 8A power supply to power devices in the rack. Right now, it powers my modem, router, and GS105 switch, but there are 4 additional channels which could be used for an SBC, hard drive mount, etc.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/rack-v1.BYbkKHVL_Z1gUnNC.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;proxmox&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#proxmox&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Proxmox&lt;/h1&gt;
&lt;p&gt;After using the rack for some time, I finally broke down and spent the money on an actual server. I went with a refurbished Lenovo ThinkCentre M710q that I bought for around $75, since it had relatively modern hardware, would fit within a 10” 1U shelf, and uses relatively low power.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3029&quot; height=&quot;2818&quot; src=&quot;https://breq.dev/_astro/grapefruit.Dv73WjiZ_ZumVv8.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;A few friends recommended I try &lt;a href=&quot;https://www.proxmox.com/&quot;&gt;Proxmox&lt;/a&gt; for this project, so I decided to give it a try! It supports full VMs as well as containers, which is nice. Previously I’ve relied on solutions like Dokku which only support container-level virtualization.&lt;/p&gt;
&lt;h2 id=&quot;unifi-network-controller&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#unifi-network-controller&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;UniFi Network Controller&lt;/h2&gt;
&lt;p&gt;Shortly after I moved, I &lt;a href=&quot;https://breq.dev/2024/10/22/home-network&quot;&gt;invested in a Ubiquiti access point&lt;/a&gt; for the new apartment, and I absolutely do not regret my decision. However, this AP has one minor annoyance for home use compared to a traditional residential access point. Instead of being configured via a web UI running locally on the device, Ubiquiti APs are configured by a central server. This approach is great for managing a large deployment of access points but slightly annoying for a home setup.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1493&quot; height=&quot;961&quot; src=&quot;https://breq.dev/_astro/unifi.BmpDhzFZ_KDd0H.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;However, there’s an upside: Ubiquiti’s controller software can be &lt;a href=&quot;https://help.ui.com/hc/en-us/articles/220066768-Updating-and-Installing-Self-Hosted-UniFi-Network-Servers-Linux&quot;&gt;self-hosted rather easily&lt;/a&gt;! I decided to do this first, and after spending 5 minutes trying to find where to install container templates from, I got the Unifi controller running on an Ubuntu container. After &lt;a href=&quot;https://help.ui.com/hc/en-us/articles/360008976393-Backups-and-Migration-in-UniFi&quot;&gt;exporting and importing&lt;/a&gt; my config to transfer it to the new controller, I can finally manage access point settings without needing to boot my desktop!&lt;/p&gt;
&lt;h2 id=&quot;homeassistant&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#homeassistant&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;HomeAssistant&lt;/h2&gt;
&lt;p&gt;Ava is a big fan of smart home devices, so we’ve ended up with a dozen smart light bulbs in the apartment. While running these through Google Home works great for basic usage, the lack of an extensible API is something we’ve missed dearly. Our home has already started to collect &lt;a href=&quot;https://breq.dev/projects/matrix2&quot;&gt;weird and wonderful homemade devices&lt;/a&gt;, and an open platform would give us much greater opportunity for fun shenanigans.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1493&quot; height=&quot;961&quot; src=&quot;https://breq.dev/_astro/homeassistant.B_wp5wJJ_Z1mgWan.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;On the recommendation of a friend, we’re trying out &lt;a href=&quot;https://www.home-assistant.io/&quot;&gt;HomeAssistant&lt;/a&gt; to run our devices. Most of our devices are based on the &lt;a href=&quot;https://www.home-assistant.io/integrations/matter/&quot;&gt;Matter&lt;/a&gt; standard, which should (in theory) enable fully local control. The process of importing Matter devices went pretty smoothly, although we needed to tediously rename each device we added. We do have a few weird bulbs we bought to fit in an IKEA fixture that used the Tuya app, but HomeAssistant had an integration for those as well which seems to work perfectly.&lt;/p&gt;
&lt;h2 id=&quot;reverse-proxy&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#reverse-proxy&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Reverse Proxy&lt;/h2&gt;
&lt;p&gt;With all of these services on the same box, I’ll definitely need to set up a reverse proxy for these services. My go-to for this is usually &lt;a href=&quot;https://nginx.org/&quot;&gt;nginx&lt;/a&gt;, but I decided to try &lt;a href=&quot;https://caddyserver.com/&quot;&gt;Caddy&lt;/a&gt; instead to avoid the hassle of setting up HTTPS certificates. While I did find it more annoying to debug, adding new hosts and proxying new services is turning out to be a breeze so far!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1493&quot; height=&quot;961&quot; src=&quot;https://breq.dev/_astro/proxy.BVYKsK6D_Z2f2I0I.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The reverse proxy also serves a basic static site, mostly as an excuse for me to make a cute drawing of the rack and my desktop.&lt;/p&gt;
&lt;h2 id=&quot;syncthing&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#syncthing&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Syncthing&lt;/h2&gt;
&lt;p&gt;For my “hot workspace” of files (documents, code, etc), I like to store things in &lt;a href=&quot;https://syncthing.net/&quot;&gt;Syncthing&lt;/a&gt; so they’re available on each of my devices. It’s the best cross-platform tool for file sync that I’ve found, and is really easy to self-host!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1493&quot; height=&quot;961&quot; src=&quot;https://breq.dev/_astro/syncthing.BVcSZhs0_Z503kB.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I created a container and followed the Syncthing setup steps for Ubuntu. The only snag I hit was that I couldn’t remotely login to set up the GUI from a different device. Turns out the approach is to edit the config to replace &lt;code&gt;127.0.0.1&lt;/code&gt; with &lt;code&gt;0.0.0.0&lt;/code&gt;, which is pretty straightforward:&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;nano /root/.local/state/syncthing/config.xml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The documentation for &lt;a href=&quot;https://docs.syncthing.net/v1.0.0/users/autostart.html#linux&quot;&gt;setting it up to run with systemd&lt;/a&gt; also worked perfectly.&lt;/p&gt;
&lt;h2 id=&quot;nas&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#nas&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;NAS&lt;/h2&gt;
&lt;p&gt;I tend to keep only actively used files on Syncthing since my devices are constantly running out of storage space. For long term storage, Ava and I used to use NextCloud, but found it annoying to keep it running and stable. Thus, we switched to a basic Samba and WebDAV share for this.&lt;/p&gt;
&lt;p&gt;While I don’t immediately have hard drives ready to add, I was able to get started by just setting up a container and using a folder on its root volume. Installing Samba was straightforward. I do eventually want to install WebDAV as well, but that will have to wait for another time (I’ve been sitting on this blog post for far too long!)&lt;/p&gt;
&lt;h2 id=&quot;octoprint&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#octoprint&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;OctoPrint&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://octoprint.org/&quot;&gt;OctoPrint&lt;/a&gt; is a server for managing a 3D printer remotely. I recently dusted off my old Ender 3 Pro and started &lt;a href=&quot;https://breq.dev/projects/matrix2&quot;&gt;making stuff with it again&lt;/a&gt;, and now that I’ve graduated and work a normal work schedule, I have fewer opportunities to check on a print throughout the day.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1493&quot; height=&quot;961&quot; src=&quot;https://breq.dev/_astro/octoprint.CyBK5n-k_Z1ghzdf.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I decided to use the popular &lt;a href=&quot;https://github.com/paukstelis/octoprint_deploy&quot;&gt;octoprint_deploy&lt;/a&gt; for installation. Since the README warns against trying to deploy in an LXC container, I made this host a full Ubuntu Server VM. I was able to pass through the USB device easily by its vendor and product ID, and everything worked on the first try! I don’t have a USB camera for remotely monitoring yet, but I might decide to add one if I find this useful.&lt;/p&gt;
&lt;h2 id=&quot;general-purpose-ubuntu-and-windows-servers&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#general-purpose-ubuntu-and-windows-servers&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;General purpose Ubuntu and Windows servers&lt;/h2&gt;
&lt;p&gt;I find myself occasionally booting into Windows 11 for things, most recently for uploading a codeplug to my OpenGD77 radio. Since I almost never use this partition otherwise (the video games I play all support Linux at this point), I really only need a basic Windows 11 box with USB passthrough support. I got the idea for this from my girlfriend &lt;a href=&quot;https://adryd.com/&quot;&gt;Ari&lt;/a&gt; who uses a Windows VM for similar purposes. I also set up an Ubuntu desktop machine at the same time, mostly because it was easy to do so.&lt;/p&gt;
&lt;h1 id=&quot;results-and-conclusions&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results-and-conclusions&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results and Conclusions&lt;/h1&gt;
&lt;p&gt;First off, I would like to think Ava for putting up with me occasionally disrupting our home internet to perform upgrades or maintenance. Thanks also to &lt;a href=&quot;https://adryd.com/&quot;&gt;Ari&lt;/a&gt;, &lt;a href=&quot;https://pixilic.com/&quot;&gt;Hunter&lt;/a&gt; and &lt;a href=&quot;https://tris.fyi/&quot;&gt;Tris&lt;/a&gt; for helping me troubleshoot on the countless occasions I accidentally shot myself in the foot with Proxmox. This project veered farther into the realm of networking and system administration than I ever had before, and I couldn’t have done it without help.&lt;/p&gt;
&lt;p&gt;Taken as a whole, this is probably one of my more practical projects as of late: I’m finally able to clean up my convoluted file sync situation, I’ve gained the ability to remotely manage stuff on my home network, and I’ve already enabled a few helpful automations with HomeAssistant.&lt;/p&gt;</content:encoded></item><item><title>orb.breq.dev</title><link>https://breq.dev/projects/orb/</link><guid isPermaLink="true">https://breq.dev/projects/orb/</guid><description>Web-based control of my desk light using HomeAssistant</description><pubDate>Tue, 29 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4096&quot; height=&quot;3072&quot; src=&quot;https://breq.dev/_astro/fado.C8E77hjW_Z13u14R.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Sitting right next to my desk as I type this is an &lt;a href=&quot;https://www.ikea.com/us/en/p/fado-table-lamp-white-70096377/&quot;&gt;IKEA FADO&lt;/a&gt; lamp fitted with an RGBW LED bulb. Among friends, it’s become affectionately known as “the orb” due to its unique shape.&lt;/p&gt;
&lt;h1 id=&quot;implementation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#implementation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Implementation&lt;/h1&gt;
&lt;p&gt;This project uses quite a few moving parts to pipe lighting data through from the user’s browser into a smart light bulb. I’ll trace the flow through each of these components.&lt;/p&gt;
&lt;p&gt;The user chooses a color through a basic web UI written in vanilla HTML, CSS, and JS. There’s a sketch of the orb that I made in &lt;a href=&quot;https://inkscape.org/&quot;&gt;Inkscape&lt;/a&gt; to provide a preview.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;559&quot; height=&quot;544&quot; src=&quot;https://breq.dev/_astro/ui.CgKwtIiS_1w6vDn.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The UI makes calls to a &lt;a href=&quot;https://flask.palletsprojects.com/en/stable/&quot;&gt;Flask&lt;/a&gt;-based backend, which turns around and calls HomeAssistant. This layer exists only to allow unauthenticated users to call these two specific methods on the HomeAssistant API (since the Flask app keeps a bearer token around). I deployed this on &lt;a href=&quot;https://dokku.com/&quot;&gt;Dokku&lt;/a&gt;, which is a stack &lt;a href=&quot;https://breq.dev/2021/02/10/dokku&quot;&gt;I’ve developed a strong familiarity with&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While I initially tried to make this project work with Google Home, I couldn’t find an API that would allow me to invoke commands programmatically. Ava and I had been looking for an excuse to migrate to HomeAssistant for a while, and this was it! We found the setup process to be quite straightforward: most of our smart devices are Matter bulbs, which could easily be added to both Google Home and HomeAssistant.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;580&quot; height=&quot;349&quot; src=&quot;https://breq.dev/_astro/homeassistant.c9F5JEB__2tF80T.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;HomeAssistant gives each entity an “Entity ID”, which makes programming easy. The orb light is just &lt;code&gt;lights.orb&lt;/code&gt;! While finding the right documentation was a bit difficult, invoking actions on entities through the REST API was overall easy and straightforward.&lt;/p&gt;
&lt;p&gt;We’ve deployed HomeAssistant to a Proxmox server running in &lt;a href=&quot;https://breq.dev/projects/minirack&quot;&gt;our home mini-rack&lt;/a&gt;. Since our HomeAssistant instance is publicly routable at &lt;code&gt;homeassistant.home.breq.dev&lt;/code&gt;, no special considerations were needed for piping together the Flask app deployed in the cloud with HomeAssistant running in our home network.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;In and of itself, the orb demo isn’t that interesting, especially because you need to actually be in front of the light to see it working (and if you’re already physically in my apartment, you can just use the Google Home to set the light). That said, it’s a fun party trick, and I enjoyed finally being able to interact with my smart home devices easily from code! I’m definitely looking forward to doing more smart home projects in the future.&lt;/p&gt;</content:encoded></item><item><title>Greatest Hits of the MOS 6502</title><link>https://breq.dev/2025/04/23/6502/</link><guid isPermaLink="true">https://breq.dev/2025/04/23/6502/</guid><description>My favorite quirks, errata, and fun uses of processor features from the 6502 era.</description><pubDate>Wed, 23 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Over the years, I’ve developed a strong appreciation for the MOS 6502, mostly through my work &lt;a href=&quot;https://breq.dev/2022/11/26/noentiendo&quot;&gt;writing an emulator&lt;/a&gt; (something I feel &lt;a href=&quot;https://breq.dev/2024/09/14/emulator&quot;&gt;everyone should do&lt;/a&gt;). In researching this chip, I’ve stumbled across countless creative tricks and workarounds both in the design of the chip and the vast library of software written for it.&lt;/p&gt;
&lt;h1 id=&quot;zero-page&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#zero-page&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Zero Page&lt;/h1&gt;
&lt;p&gt;This one’s more of an interesting design choice: the 6502 has an absolutely tiny register file. The only general-purpose register is “A”, the accumulator. There are two additional registers “X” and “Y”, but they are only used for indexing memory.&lt;/p&gt;
&lt;p&gt;However, the designers of the 6502 realized that this was extremely limiting to programs and would require lots of reads and writes into memory to handle temporary values computed in a program. Using the “Absolute” addressing mode, this requires two bytes for the memory address and around 4 cycles depending on the instruction. The “Zero Page” addressing mode is a hack: when a Zero Page instruction is used, only the lower byte of the memory address is stored, and the processor can skip a cycle! This both reduces the program size and speeds up program execution. As a result, the first 256 bytes of memory are effectively “registers” of sorts!&lt;/p&gt;
&lt;h1 id=&quot;the-cps-pin&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-cps-pin&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The “CPS pin”&lt;/h1&gt;
&lt;p&gt;Like many architectures, the 6502 has a status register with flags like “zero”, “overflow”, and “negative” representing the last operation completed with the ALU. Unlike these architectures, though, the 6502 has a fun quirk: the chip has a physical “SO” (“set overflow”) pin which, when the signal on it goes from high to low, sets the overflow flag in the status register. This flag can be used by subsequent branch instructions.&lt;/p&gt;
&lt;p&gt;The usefulness of this pin is questionable, and it rarely saw usage in practice. It was apparently used in tight loops for routines interfacing with hardware, since a transition on the pin could cause code to jump out of the loop. The pin &lt;a href=&quot;http://www.6502.org/tutorials/vflag.html&quot;&gt;was originally called the CPS pin&lt;/a&gt;, short for “Chuck Peddle Special” (named after the main designer of the 6502, &lt;a href=&quot;https://en.wikipedia.org/wiki/Chuck_Peddle&quot;&gt;Chuck Peddle&lt;/a&gt;).&lt;/p&gt;
&lt;h1 id=&quot;the-brk-instruction&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-brk-instruction&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The BRK Instruction&lt;/h1&gt;
&lt;p&gt;On the 6502, opcode &lt;code&gt;00&lt;/code&gt; maps to the “BRK” instruction. This is essentially used to trigger an interrupt from within the program.&lt;/p&gt;
&lt;p&gt;In the 6502, the location in memory that code is executed from after a reset or interrupt is controlled by a table of “vectors” located at the very end of the memory map.&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Signal&lt;/th&gt;&lt;th&gt;Location&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;NMI&lt;/td&gt;&lt;td&gt;FFFA-FFFB&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;RESET&lt;/td&gt;&lt;td&gt;FFFC-FFFD&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;IRQ/BRK&lt;/td&gt;&lt;td&gt;FFFE-FFFF&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;NMI and IRQ are for the two pins on the microprocessor of the same name (“non-maskable interrupt” and “interrupt request” respectively). RESET is for when the microcontroller boots up or is reset. BRK shares its vector with IRQ, meaning the code routine which services maskable interrupts is the same routine which services BRK instructions. So, when you run BRK, your code immediately jumps to the interrupt routine. Once the interrupt routine returns with the RTI instruction (“Return From Interrupt”), your code keeps executing.&lt;/p&gt;
&lt;p&gt;BRK was &lt;em&gt;intended&lt;/em&gt; for use in debugging (as a “breakpoint” of sorts). In some systems, this was what happened: the Apple II would launch the &lt;a href=&quot;https://gunkies.org/wiki/Apple_II_Machine_Language_Monitor&quot;&gt;program monitor&lt;/a&gt; which would allow you to debug your program. However, this instruction has a weird quirk: despite BRK being a 1-byte instruction, when the interrupt routine returned, the program counter would jump forward by 2 bytes instead of 1 — leaving a single byte after the BRK instruction unused. Clever programmers could exploit this by looking at the top of the stack and reading this value, using it to pass state between the code calling BRK and the interrupt service routine.&lt;/p&gt;
&lt;p&gt;On the BBC Micro, &lt;a href=&quot;https://stardot.org.uk/mirrors/www.bbcdocs.com/filebase/essentials/BBC%20Microcomputer%20Advanced%20User%20Guide.pdf&quot;&gt;BRK was used for error handling&lt;/a&gt;. To throw an error, the BRK instruction would be called with the error code stored in the byte immediately following it. Since returning from an error handler was not supported, an error string could be stored immediately after the error code byte additionally.&lt;/p&gt;
&lt;p&gt;On the Apple III, Apple’s Sophisticated Operating System used &lt;a href=&quot;https://apple3.org/Documents/Manuals/Apple%20III%20SOS%20Reference%20Manual%20Volume%202%20-%20The%20SOS%20Calls.PDF&quot;&gt;BRK for system calls&lt;/a&gt;. The system call opcode would be placed immediately after the BRK instruction, then a 2-byte pointer to additional parameters. This did mean the SOS routine responsible for system calls needed to increment the return address by 2.&lt;/p&gt;
&lt;h1 id=&quot;wozniaks-sweet16&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#wozniaks-sweet16&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Wozniak’s SWEET16&lt;/h1&gt;
&lt;p&gt;The 6502 is an 8-bit processor, but addresses and other values are often 16 bits. While it’s of course possible to manipulate these larger values using a combination of 8-bit operations, the resulting code is often quite verbose. Steve Wozniak, known for his obsession with going great lengths to save a small amount of resources, did not want to deal with this, so he created an &lt;em&gt;interpreted bytecode&lt;/em&gt; language called &lt;a href=&quot;https://archive.org/details/BYTE_Vol_02-11_1977-11_Sweet_16/page/n151/mode/2up&quot;&gt;SWEET16&lt;/a&gt;. A program could call into a subroutine, and the code after the subroutine call would be interpreted as SWEET16 instructions until the SWEET16 “Return” instruction was executed, at which point the following code is executed normally.&lt;/p&gt;
&lt;p&gt;SWEET16 is an interesting tradeoff for code size versus performance. While it massively reduces the size of code, interpreted SWEET16 code is about one tenth the speed of native 6502 code. While optimizing for code size was usually preferred in the past, modern processors have improved slower than modern memory has, so this tradeoff is less relevant now than it once was.&lt;/p&gt;
&lt;p&gt;SWEET16 supported several “nonregister operations”. These included branch operations, which used a 1-byte signed offset as the branch target.&lt;/p&gt;











































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Opcode&lt;/th&gt;&lt;th&gt;Mnemonic&lt;/th&gt;&lt;th&gt;Function&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;00&lt;/td&gt;&lt;td&gt;RTN&lt;/td&gt;&lt;td&gt;Exit SWEET16, return to native 6502 code&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;01&lt;/td&gt;&lt;td&gt;BR ea&lt;/td&gt;&lt;td&gt;Branch always (i.e., jump)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;02&lt;/td&gt;&lt;td&gt;BNC ea&lt;/td&gt;&lt;td&gt;Branch if No Carry&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;03&lt;/td&gt;&lt;td&gt;BC ea&lt;/td&gt;&lt;td&gt;Branch if Carry&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;04&lt;/td&gt;&lt;td&gt;BP ea&lt;/td&gt;&lt;td&gt;Branch if Plus&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;05&lt;/td&gt;&lt;td&gt;BM ea&lt;/td&gt;&lt;td&gt;Branch if Minus&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;06&lt;/td&gt;&lt;td&gt;BZ ea&lt;/td&gt;&lt;td&gt;Branch if Zero&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;07&lt;/td&gt;&lt;td&gt;BNZ ea&lt;/td&gt;&lt;td&gt;Branch if Non-Zero&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;08&lt;/td&gt;&lt;td&gt;BM1 ea&lt;/td&gt;&lt;td&gt;Branch if Minus 1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;09&lt;/td&gt;&lt;td&gt;BNM1 ea&lt;/td&gt;&lt;td&gt;Branch if Not Minus 1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0A&lt;/td&gt;&lt;td&gt;BK&lt;/td&gt;&lt;td&gt;Break&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0B&lt;/td&gt;&lt;td&gt;RS&lt;/td&gt;&lt;td&gt;Return from Subroutine&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;0C&lt;/td&gt;&lt;td&gt;BS ea&lt;/td&gt;&lt;td&gt;Branch to Subroutine&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;It also featured 16 “registers”, stored in the zero page. These were specified using the second nibble of the opcode, allowing these instructions to take up only a single byte. Notably, some instructions supported an indirect addressing mode. Many operate on register R0, also called the accumulator.&lt;/p&gt;





















































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Opcode&lt;/th&gt;&lt;th&gt;Mnemonic&lt;/th&gt;&lt;th&gt;Function&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1n&lt;/td&gt;&lt;td&gt;SET Rn $xxxx&lt;/td&gt;&lt;td&gt;Set a register to an immediate value&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2n&lt;/td&gt;&lt;td&gt;LD Rn&lt;/td&gt;&lt;td&gt;Transfer the value in the specified register to R0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3n&lt;/td&gt;&lt;td&gt;ST Rn&lt;/td&gt;&lt;td&gt;Transfer the value in R0 to the specified register&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4n&lt;/td&gt;&lt;td&gt;LD @Rn&lt;/td&gt;&lt;td&gt;Load the low-order byte of R0 from the memory address stored in Rn, then increment the value in Rn&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5n&lt;/td&gt;&lt;td&gt;ST @Rn&lt;/td&gt;&lt;td&gt;Store the low-order byte of R0 into the memory address stored in Rn, then increment the value in Rn&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6n&lt;/td&gt;&lt;td&gt;LDD @Rn&lt;/td&gt;&lt;td&gt;Load a 16-bit little-endian value from the memory location stored in Rn into R0, then increment the value in Rn by 2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7n&lt;/td&gt;&lt;td&gt;STD @Rn&lt;/td&gt;&lt;td&gt;Store a 16-bit little-endian value from R0 into the memory location stored in Rn, then increment the value in Rn by 2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;8n&lt;/td&gt;&lt;td&gt;POP @Rn&lt;/td&gt;&lt;td&gt;Decrement the value in Rn, then load the low-order byte of R0 from the memory address stored in Rn&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;9n&lt;/td&gt;&lt;td&gt;STP @Rn&lt;/td&gt;&lt;td&gt;Decrement the value in Rn, then store the low-order byte of R0 to the memory address stored in Rn&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;An&lt;/td&gt;&lt;td&gt;ADD Rn&lt;/td&gt;&lt;td&gt;Add the contents of Rn to R0, and store the result in R0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Bn&lt;/td&gt;&lt;td&gt;SUB Rn&lt;/td&gt;&lt;td&gt;Subtract the contents of Rn from R0, and store the result in R0&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cn&lt;/td&gt;&lt;td&gt;POPD @Rn&lt;/td&gt;&lt;td&gt;Decrement the value in Rn by 2, then load the 16-bit little-endian value from R0 into the memory address stored in Rn&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Dn&lt;/td&gt;&lt;td&gt;CPR Rn&lt;/td&gt;&lt;td&gt;Subtract the value in Rn from R0, and set the status flags for branching&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;En&lt;/td&gt;&lt;td&gt;INR Rn&lt;/td&gt;&lt;td&gt;Increment the contents of Rn&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Fn&lt;/td&gt;&lt;td&gt;DCR Rn&lt;/td&gt;&lt;td&gt;Decrement the contents of Rn&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;SWEET16 stands out to me as a shockingly complete interpreted language implemented in a shockingly short amount of code. It’s something I otherwise wouldn’t have been expected to be feasible in this context.&lt;/p&gt;
&lt;h1 id=&quot;illegal-instructions-and-their-uses&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#illegal-instructions-and-their-uses&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Illegal instructions and their uses&lt;/h1&gt;
&lt;p&gt;The 6502 has 151 valid opcodes. However, there are 256 possible values that the first byte of an instruction can take. What happens if you try one of the invalid ones?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;12 of them just lock up the CPU and prevent it from executing instructions further&lt;/li&gt;
&lt;li&gt;27 of them are no-ops, with varying instruction lengths&lt;/li&gt;
&lt;li&gt;1 has non-deterministic behavior (“XAA”, which I won’t even try to describe here)&lt;/li&gt;
&lt;li&gt;The rest are perfectly usable, but do some &lt;a href=&quot;https://www.pagetable.com/c64ref/6502/?tab=2&quot;&gt;rather strange operations&lt;/a&gt;…&lt;/li&gt;
&lt;/ul&gt;













































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Mnemonic&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;ANC&lt;/td&gt;&lt;td&gt;AND memory with accumulator then move negative flag to carry flag&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ARR&lt;/td&gt;&lt;td&gt;AND memory with accumulator then rotate right&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ASR&lt;/td&gt;&lt;td&gt;AND memory with accumulator then logical shift right&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;DCP&lt;/td&gt;&lt;td&gt;Decrement memory by 1 then compare with accumulator&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ISC&lt;/td&gt;&lt;td&gt;Increment memory by 1 then subtract from accumulator, store result in accumulator&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;LAS&lt;/td&gt;&lt;td&gt;AND memory with stack pointer, store result in accumulator, X register, and stack pointer&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;LAX&lt;/td&gt;&lt;td&gt;Load both accumulator and index register from memory&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;RLA&lt;/td&gt;&lt;td&gt;Rotate memory left then AND with accumulator&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;RRA&lt;/td&gt;&lt;td&gt;Rotate memory right then add to accumulator&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SAX&lt;/td&gt;&lt;td&gt;Store accumulator AND X register into memory&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SBX&lt;/td&gt;&lt;td&gt;Subtract memory from accumulator AND X register, store in X&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SHA&lt;/td&gt;&lt;td&gt;Store accumulator AND index register AND a value dependent on the addressing mode into memory&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SHS&lt;/td&gt;&lt;td&gt;AND accumulator with X register, store in stack pointer, then store stack pointer AND high byte of memory address into memory&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SHX&lt;/td&gt;&lt;td&gt;Store index register X AND upper byte of address plus 1 into memory&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SHY&lt;/td&gt;&lt;td&gt;Store index register Y AND upper byte of address plus 1 into memory&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SLO&lt;/td&gt;&lt;td&gt;Shift memory left, then store memory OR accumulator into accumulator&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;SRE&lt;/td&gt;&lt;td&gt;Shift memory right, then store memory XOR accumulator into accumulator&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The technical reasons for why this behavior happens are described well in &lt;a href=&quot;https://www.masswerk.at/nowgobang/2021/6502-illegal-opcodes&quot;&gt;this blog post&lt;/a&gt; and &lt;a href=&quot;https://codebase64.org/lib/exe/fetch.php?media=base:nomoresecrets-nmos6510unintendedopcodes-20172412.pdf&quot;&gt;this document&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A few of these instructions were used in &lt;a href=&quot;https://www.nesdev.org/wiki/CPU_unofficial_opcodes#Games_using_unofficial_opcodes&quot;&gt;a few NES games&lt;/a&gt; (mostly 2-byte NOPs). These also saw use on various &lt;a href=&quot;https://github.com/mattgodbolt/jsbeeb/issues/5&quot;&gt;BBC Micro games&lt;/a&gt;. Games used these for both copy protection (as an attempt to validate the hardware?) and for performance. My guess is that more serious software doesn’t bother with these since performance is less of a concern than correctness.&lt;/p&gt;
&lt;p&gt;Later revisions of the 6502, such as the &lt;a href=&quot;https://www.westerndesigncenter.com/wdc/documentation/w65c02s.pdf&quot;&gt;W65C02S&lt;/a&gt;, replaced the opcodes in the “F” column with additional bit manipulation opcodes.&lt;/p&gt;
&lt;h1 id=&quot;the-ricoh-2a032a07-and-the-nes&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-ricoh-2a032a07-and-the-nes&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Ricoh 2A03/2A07 and the NES&lt;/h1&gt;
&lt;p&gt;The 6502 was incredibly popular, and inspired a few clones and derivatives. One such clone was the Ricoh 2A03/2A07 chip used in the Nintendo Entertainment System. Apparently, Nintendo was not a big believer in copyright law back in this era. Oh, how the times have changed…&lt;/p&gt;
&lt;p&gt;To see how blatant this was, first look at this image of the 6502 chip (courtesy of &lt;a href=&quot;http://www.visual6502.org/images/6502/index.html&quot;&gt;Visual 6502&lt;/a&gt;)…&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4677&quot; height=&quot;5097&quot; src=&quot;https://breq.dev/_astro/6502_top_op10x_BF_4677.C3pSJ19N_ZEKAVV.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now, look at this image of the Ricoh 2A03 (also from &lt;a href=&quot;http://www.visual6502.org/images/pages/Nintendo_RP2A_die_shots.html&quot;&gt;Visual 6502&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1600&quot; height=&quot;1573&quot; src=&quot;https://breq.dev/_astro/Nintendo_RP2A03G_die_shot_1a_1600w.Bt80ndjH_1IxqxH.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Does that little bit in the lower right corner look familiar?&lt;/p&gt;
&lt;p&gt;Interestingly, the Binary Coded Decimal (BCD) functionality of the 6502 were fused off before the chip was incorporated into the Ricoh clone. This feature allowed the 6502 to perform arithmetic operations on numbers stored as decimal, with 4 bits representing a decimal digit. It was covered under a &lt;a href=&quot;https://patents.google.com/patent/US3991307&quot;&gt;patent held by MOS&lt;/a&gt;, which is probably why Ricoh disabled it.&lt;/p&gt;
&lt;p&gt;The other parts of this chip are for things like the NES’s sound generator, known as the “APU” and definitely worthy of its own post someday. It’s also why there are two variants of this chip: one for NTSC and one for PAL.&lt;/p&gt;
&lt;h1 id=&quot;conclusion&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;The 6502 is one of the final bastions of an age where microprocessors could be contained in a pile of parchment in a dude named Chuck’s desk. It was one of the first mainframes in a chip, and powered countless number of people’s first experiences in computing. The number of programmers today who got their start on the 6502 alone makes the chip worldchanging in it of itself.&lt;/p&gt;</content:encoded></item><item><title>A &quot;5V Bypass Mod&quot; for the PI040202-7X2C PCIe Card</title><link>https://breq.dev/2025/04/09/usb-pcie-5v-bypass/</link><guid isPermaLink="true">https://breq.dev/2025/04/09/usb-pcie-5v-bypass/</guid><description>Unlock higher current draw on this &quot;quad-chip&quot; PCIe USB card with a few simple steps!</description><pubDate>Wed, 09 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The article below was republished from the internal &lt;a href=&quot;https://www.northeasternrover.com/&quot;&gt;NURover&lt;/a&gt; Notion wiki. On the Rover team, we use a PCIe to USB card with four individual USB controllers to provide enough bandwidth for our camera streaming system (something I’ve &lt;a href=&quot;https://breq.dev/2023/06/21/cameras&quot;&gt;posted about previously&lt;/a&gt;). As you can probably imagine, the USB system on the rover has very high current draw, and we’ve fried cards (quite dramatically) in the past.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Some PCIe to USB cards require a modification to pass through a high amount of current on the 5V rail. This page describes how to apply the modification.&lt;/p&gt;
&lt;h1 id=&quot;do-i-need-to-perform-a-5v-bypass-mod&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#do-i-need-to-perform-a-5v-bypass-mod&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Do I need to perform a 5V Bypass Mod?&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;There are two types of “quad chip” PCIe card available&lt;/strong&gt;: “B” variant and “C” variant.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1461&quot; height=&quot;712&quot; src=&quot;https://breq.dev/_astro/e085f25e-cfcc-45a3-9550-c0660acb77dd.CWcZd62j_ZvlVfU.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;PI040202-7X2B (”B” variant)&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1423&quot; height=&quot;653&quot; src=&quot;https://breq.dev/_astro/0f0c30a1-d1f2-443f-99b4-86e812f627fc.Gg_arJaM_2iRrob.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;PI040202-7X2C (”C” variant)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This modification is only required for the “C” variant.&lt;/strong&gt; The “B” variant boards provide power from the +5V pin of the Molex or SATA power connector directly to the USB port (after some LC filtering). The “C” variant boards instead provide power from the +12V pin on the power connector, passed through a voltage regulator chip. This chip cannot handle the high current loads present on the rover.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Once performing this modification, the card can no longer be powered from the PCIe slot and will REQUIRE external power to operate.&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&quot;removing-the-voltage-regulators&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#removing-the-voltage-regulators&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Removing the Voltage Regulators&lt;/h1&gt;
&lt;p&gt;The first step is to remove the 12V to 5V voltage regulator chips. There are two ways to do this.&lt;/p&gt;
&lt;h2 id=&quot;hot-air&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#hot-air&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Hot Air&lt;/h2&gt;
&lt;p&gt;The least destructive method of removal is the use of a hot air workstation.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set the hot air gun to a temperature of approximately 450 F and turn the fan up.&lt;/li&gt;
&lt;li&gt;Apply heat to the chip.&lt;/li&gt;
&lt;li&gt;Once the chip is heated, use a small flathead screwdriver to push it out of position and away from its footprint.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;image.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1061&quot; height=&quot;1057&quot; src=&quot;https://breq.dev/_astro/e160f4a6-aff7-40b0-90f8-bcbe4287a7f3.BL490jjX_SEmzD.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Notes on this method:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Note that if you do not push the chip completely off of the footprint, it may reattach to a different set of pads. Just try again!&lt;/li&gt;
&lt;li&gt;It is okay if the components surrounding the chip start to move a bit — the soldermask and the surface tension of the solder should keep them roughly in place. Even if knocked off, components are for filtering the &lt;em&gt;input&lt;/em&gt; side of the voltage regulator and are thus not needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;cutting-tools&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#cutting-tools&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Cutting Tools&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;image.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;642&quot; height=&quot;542&quot; src=&quot;https://breq.dev/_astro/7367cbf9-aac6-4855-963c-e78b7c46d75e.B5F55g1u_ZLKR5k.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;570&quot; height=&quot;519&quot; src=&quot;https://breq.dev/_astro/5b5779b6-8259-4ca8-a22a-19fed2c2ea5a.wP7QcjwG_Z2qLHQO.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;If the chip is burnt, you can use flush cutters to remove it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Remove the plastic casing of the chip by scraping it off.&lt;/li&gt;
&lt;li&gt;Using flush cutters, cut the legs of the chip off.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notes on this method:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make sure to avoid lifting pads! If the pads are lifted from the board too much, they can flop around and touch each other and cause shorts. Trim the pads as far back as possible.&lt;/li&gt;
&lt;li&gt;This method gives you much less room between the pads of the chip and the side of the inductor you want to solder to, which makes soldering more difficult.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;attaching-jumpers&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#attaching-jumpers&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Attaching Jumpers&lt;/h1&gt;
&lt;p&gt;You want to bridge the +5V input from the Molex connector to the two +5V outputs that would have been coming from the regulator.&lt;/p&gt;
&lt;p&gt;Your first instinct may be to solder to the 5V output pad where the regulator used to be. This is possible, but difficult to do by hand. A much easier approach is to solder to the input side of the inductor!&lt;/p&gt;
&lt;p&gt;Drop a blob of solder on the side of the inductor. The picture shows slightly too much, I cleaned it up after. Don’t worry too much about making it pretty.&lt;/p&gt;
&lt;p&gt;Cut a small length of wire (I used 24 gauge) and strip a bit at the end. Tin the end with more solder.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;535&quot; height=&quot;504&quot; src=&quot;https://breq.dev/_astro/21e3f9cc-cd1b-4498-b919-9fa7019b4993.Ch5s4bxJ_1OKYfC.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Finally, use one hand to push the wire into position and the other hand with the soldering iron to heat the pad. It may take a while for the inductor to get up to temperature — be patient!&lt;/p&gt;
&lt;p&gt;The reason we were liberal with solder before is it frees up one of your hands — you don’t need to hold the solder while attaching the wire.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;852&quot; height=&quot;889&quot; src=&quot;https://breq.dev/_astro/8c7e1d77-6e32-4a8d-b544-5ae997b00e72.DD1VkohN_ZCPrwU.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Repeat the process for the second inductor, attaching a second wire.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1067&quot; height=&quot;986&quot; src=&quot;https://breq.dev/_astro/234fe823-d364-4704-8939-986ff1efcdbb.D6shYn-L_Z1RA3cj.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Trim the wires to length — you want them to attach to the +5V pin on the Molex connector (furthest from the PCIe connector on the card). You can line them up perpendicular style like so to attach both easily.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2076&quot; height=&quot;1564&quot; src=&quot;https://breq.dev/_astro/image1.C_OGaoU5_Z2bR818.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Finally, add solder!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;image.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2076&quot; height=&quot;1564&quot; src=&quot;https://breq.dev/_astro/image2.Ds1i2QnJ_Z16R1c9.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;testing-for-shorts&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#testing-for-shorts&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Testing for Shorts&lt;/h1&gt;
&lt;p&gt;There are two shorts that you need to check for the presence for. The GND and 12V pins heading to where the voltage regulator was are very close to the pad on the inductor that you soldered to, and bridging is very possible. You can test this by checking connectivity between your jumper wires and the USB connector shielding (for GND) and between your jumper wires and the &lt;strong&gt;output side of diodes D1/D2 and D3/D4&lt;/strong&gt; (for 12V).&lt;/p&gt;
&lt;p&gt;If neither rail is shorted, your board is ready to use!&lt;/p&gt;
&lt;h1 id=&quot;schematic&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#schematic&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Schematic&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;image.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1336&quot; height=&quot;1513&quot; src=&quot;https://breq.dev/_astro/ff1e0c1b-6bbe-4190-a8d2-5c2ee137775c.C3rrZv1T_1p0RW4.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title>KiCAD SVG Prettifier</title><link>https://breq.dev/projects/kicad-pretty/</link><guid isPermaLink="true">https://breq.dev/projects/kicad-pretty/</guid><description>A web app to make realistic-looking SVGs of KiCAD boards</description><pubDate>Thu, 03 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3056&quot; height=&quot;1466&quot; src=&quot;https://breq.dev/_astro/kicad-pretty.CRcXKdGm_Z1ot9H1.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;This project recolors SVG exports of KiCAD circuit board designs to appear in realistic colors.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I faced an annoying problem with KiCAD the other day. I wanted to export a realistic-looking image of a PCB design to use for an upcoming project. However, the existing options for doing this didn’t fit my needs.&lt;/p&gt;
&lt;p&gt;The 3D viewer, while very cool, isn’t great for this use case. I would prefer a vector image to a raster one. Plus, with the way the lighting is set up, an “exactly head-on view” of a board can look very blown out. Here’s a board with light-colored silkscreen as viewed head-on in the 3D viewer:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1730&quot; height=&quot;1256&quot; src=&quot;https://breq.dev/_astro/too-bright.D1wawMMf_1MxpF6.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;KiCAD also has the ability to export an SVG natively, but the result looks like what you would see in the editor: the colors match the editing layers, not the actual realistic colors of the board, and the soldermask layer is inverted. Plus, I wanted a way to generate separate front and back views, with the back view mirrored to how it would appear in real life.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1123&quot; height=&quot;794&quot; src=&quot;https://breq.dev/_astro/kicad-svg.BRkYGst1_Z1C7Ygr.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;implementation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#implementation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Implementation&lt;/h1&gt;
&lt;p&gt;So, I built a tool to recolor the output from the KiCAD format to look more realistic. Annoyingly, KiCAD-generated SVGs do not put each layer into a separate SVG group or layer, so my code queries for nodes based on their existing color to apply the new colors. This would break if KiCAD exports things in an unexpected color, but since these colors are &lt;a href=&quot;https://forum.kicad.info/t/colors-in-svg-export/43668&quot;&gt;seemingly not configurable by the user&lt;/a&gt;, this should be fine.&lt;/p&gt;
&lt;p&gt;I chose to write this app in JavaScript since the language already provides good utilities for parsing, querying, and manipulating XML documents like SVG files. The UI is pretty utilitarian, but hey, it works and loads quickly! I wrote this without using a framework for styling or JavaScript — it was nice to take a break from the complex tooling of my usual React/Tailwind setup and go back to basics for a bit.&lt;/p&gt;
&lt;p&gt;The whole app is based around some &lt;em&gt;gnarly&lt;/em&gt; CSS selectors, especially since the format changes substantially between KiCAD versions and I was trying to target versions 7 through 9.&lt;/p&gt;
&lt;p&gt;Here are some example outputs for a board I’m working on:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1123&quot; height=&quot;794&quot; src=&quot;https://breq.dev/_astro/front.2UkvABG2_Z1C7Ygr.svg&quot; srcset=&quot;&quot;&gt; &lt;br&gt; &lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1123&quot; height=&quot;794&quot; src=&quot;https://breq.dev/_astro/back.BapyJM-M_Z1C7Ygr.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;try-it-yourself&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#try-it-yourself&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Try it yourself&lt;/h1&gt;
&lt;p&gt;If you want to try it out yourself, you can! First, in KiCAD, export an SVG with the relevant layers. In older versions, this is straightforward:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1508&quot; height=&quot;1316&quot; src=&quot;https://breq.dev/_astro/kicad-export.C3YIjIaR_ZPHFzQ.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;However, in the new version, they’ve replaced the “Export SVG” dialog with the “Plot” window, which exports a separate SVG file for each layer (which is not what we want!) The easiest way to get a single SVG image is with the KiCAD CLI (replace &lt;code&gt;MyBoard.kicad_pcb&lt;/code&gt; with the filename of your board file):&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;kicad-cli pcb &lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; svg --mode-single &lt;span class=&quot;token parameter variable&quot;&gt;--layers&lt;/span&gt; F.Cu,B.Cu,F.Silkscreen,B.Silkscreen,F.Mask,B.Mask,Edge.Cuts MyBoard.kicad_pcb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, go to &lt;a href=&quot;https://kicad-pretty.breq.dev/&quot;&gt;&lt;strong&gt;kicad-pretty.breq.dev&lt;/strong&gt;&lt;/a&gt; and:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Upload your SVG&lt;/li&gt;
&lt;li&gt;Change the colors as desired (e.g. if you use a different soldermask color)&lt;/li&gt;
&lt;li&gt;Click “Make Pretty!”&lt;/li&gt;
&lt;li&gt;View each preview with the “Show Front” and “Show Back” buttons&lt;/li&gt;
&lt;li&gt;Export each side with the “Export” button&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you’re having issues with the export format, feel free to reach out to me and I’ll do my best to investigate! I’ve only been able to try a handful of SVG viewers and KiCAD boards so far.&lt;/p&gt;</content:encoded></item><item><title>Who&apos;s Using breq.dev?</title><link>https://breq.dev/2025/03/10/whos-using-breqdev/</link><guid isPermaLink="true">https://breq.dev/2025/03/10/whos-using-breqdev/</guid><description>Having some fun with GitHub code search.</description><pubDate>Mon, 10 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My friend Jules made a fun suggestion on Bluesky recently:&lt;/p&gt;
&lt;bluesky uri=&quot;at://did:plc:ra3gxl2udc22odfbvcfslcn3/app.bsky.feed.post/3lk25eplens2o&quot; cid=&quot;bafyreiebisw5cbd42fmftq7tmmljyxuczmlvyo6h6xpqb3wqsfij76xqlm&quot;&gt;
&lt;p&gt;Let’s step through the &lt;a href=&quot;https://github.com/search?q=breq.dev&amp;#x26;type=code&quot;&gt;search results&lt;/a&gt; for my domain name on GitHub and see what we can find!&lt;/p&gt;
&lt;h1 id=&quot;eightyeightthirtyone&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#eightyeightthirtyone&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;eightyeightthirty.one&lt;/h1&gt;
&lt;p&gt;In 2023, some friends and I built a scraper tool called &lt;a href=&quot;https://eightyeightthirty.one/&quot;&gt;eightyeightthirty.one&lt;/a&gt; which attempted to map the entire graph of 88x31 buttons. Soon after, I wrote an article &lt;a href=&quot;https://breq.dev/2023/12/26/88x31-science&quot;&gt;applying network science to the 88x31 graph&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tantek.com/&quot;&gt;Tantek Çelik&lt;/a&gt;, the Web Standards Lead at Mozilla, liked my article enough to add it to the &lt;a href=&quot;https://indieweb.org/88x31&quot;&gt;IndieWiki page on 88x31s&lt;/a&gt;!&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sylkos.xyz/&quot;&gt;Julia Keadey&lt;/a&gt; discussed the same article in a &lt;a href=&quot;https://sylkos.xyz/blog/0&quot;&gt;blog post about her website design&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A few people credited my project page for the 88x31 scraper tool. Please credit &lt;a href=&quot;https://notnite.com/portfolio#eightyeightthirtyone&quot;&gt;NotNite’s portfolio&lt;/a&gt; instead, she did most of the work!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://sitoctt.octt.eu.org/&quot;&gt;&lt;code&gt;sitoctt&lt;/code&gt;&lt;/a&gt;’s &lt;a href=&quot;https://sitoctt.octt.eu.org/it/miscellanea/%EF%B8%8F-Raccolta-Emblemi/&quot;&gt;blog post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dongdigua.github.io/&quot;&gt;&lt;code&gt;dongdigua&lt;/code&gt;&lt;/a&gt;’s &lt;a href=&quot;https://dongdigua.github.io/internet_collections&quot;&gt;“Internet Collections”&lt;/a&gt; page&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;5f3759df&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#5f3759df&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;5F3759DF&lt;/h1&gt;
&lt;p&gt;A few people have linked to my 2021 article on the &lt;a href=&quot;https://breq.dev/2021/03/17/5F3759DF&quot;&gt;fast inverse square root algorithm&lt;/a&gt; in code over the years:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;kvn13github&lt;/code&gt;’s &lt;a href=&quot;https://github.com/kvn13github/0x5F3759DF-in-Python3-FastInverseSquareRoot/blob/main/FastInverseSquareRoot.py&quot;&gt;Python implementation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;silvernuke911&lt;/code&gt;’s &lt;a href=&quot;https://github.com/silvernuke911/Random-programs/blob/main/Fast_Inverse_Square_Root.py&quot;&gt;Python implementation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;flowspace&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#flowspace&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Flowspace&lt;/h1&gt;
&lt;p&gt;At least one person other than myself has set up a profile on &lt;a href=&quot;https://breq.dev/projects/flowspace&quot;&gt;Flowspace&lt;/a&gt;, a social network demo I made in 2021.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;R47n&lt;/code&gt;’s &lt;a href=&quot;https://r74n.com/social/&quot;&gt;website&lt;/a&gt; links to their &lt;a href=&quot;https://flowspace.breq.dev/profile/525884243050905603&quot;&gt;Flowspace profile&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;accelcoin&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#accelcoin&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Accelcoin&lt;/h1&gt;
&lt;p&gt;In 2022 I was a teaching assistant for Fundamentals of Computer Science 1 at Northeastern. One of our course assignments was for students to build a working blockchain node implementation. I ran some infrastructure relating to this! Some students have published their code to GitHub.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Shreypatel2224/BlockchainProject&quot;&gt;&lt;code&gt;Shreypatel2224&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/nishmistry/Accelcoin&quot;&gt;&lt;code&gt;nishmistry&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;wordle&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#wordle&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Wordle&lt;/h1&gt;
&lt;p&gt;In 2022 I published a few &lt;a href=&quot;https://breq.dev/projects/wordle&quot;&gt;implementations of the Wordle algorithm&lt;/a&gt; in TypeScript and Rust.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://turtywurty.dev/&quot;&gt;&lt;code&gt;turtywurty&lt;/code&gt;&lt;/a&gt;’s discord bot &lt;a href=&quot;https://github.com/DaRealTurtyWurty/SuperTurtyBot&quot;&gt;SuperTurtyBot&lt;/a&gt; includes Wordle functionality, &lt;a href=&quot;https://github.com/DaRealTurtyWurty/SuperTurtyBot/blob/d5e1499647a5eae15a596de73a57e522a175c4a8/src/main/java/dev/darealturtywurty/superturtybot/commands/minigames/WordleCommand.java#L686&quot;&gt;ported to Java&lt;/a&gt; from my implementation.&lt;/li&gt;
&lt;/ul&gt;&lt;/bluesky&gt;</content:encoded></item><item><title>Reflections on Four Years of Rover</title><link>https://breq.dev/2025/03/06/rover/</link><guid isPermaLink="true">https://breq.dev/2025/03/06/rover/</guid><description>After four years and five competitions, what have I learned?</description><pubDate>Thu, 06 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As many of you may know, I’ve been involved with the &lt;a href=&quot;https://www.northeasternrover.com/&quot;&gt;Northeastern University Mars Rover Team&lt;/a&gt; for four years now. We compete each year in the &lt;a href=&quot;https://urc.marssociety.org/&quot;&gt;University Rover Challenge&lt;/a&gt; and occasionally the &lt;a href=&quot;https://circ.cstag.ca/&quot;&gt;Canadian International Rover Challenge&lt;/a&gt;. I personally have competed in four URC events and one CIRC event, and will (hopefully!) be competing at the upcoming URC and CIRC events this summer.&lt;/p&gt;
&lt;p&gt;Here’s our latest System Acceptance Review video, which gives some context for the team structure and the subsystems we develop.&lt;/p&gt;
&lt;youtube id=&quot;9GoSA4WFsdQ&quot;&gt;
&lt;p&gt;Rover has largely dominated my college experience. Late nights in the lab have been a staple of all four years of my college life. During my co-op searches, rover has consistently been the only thing any interviewer has ever wanted to talk to me about. While the club has occasionally gotten overwhelming, I have no regrets about pouring as much energy and effort into the team as I have.&lt;/p&gt;
&lt;p&gt;This post is a collection of things I learned from my time on the team and reasons why experiences like this are valuable. This section is written from my perspective as a software developer, but the general principles apply across the engineering disciplines.&lt;/p&gt;
&lt;h1 id=&quot;thinking-across-disciplines&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#thinking-across-disciplines&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Thinking across disciplines&lt;/h1&gt;
&lt;p&gt;It’s critical to work with engineers outside the software field, for two reasons: it helps you teach others about software, and it helps you learn about other forms of engineering.&lt;/p&gt;
&lt;p&gt;As an early career software developer, you might never work with anyone outside the software field unless you seek those situations out. Even if classes require team assignments, they are typically done exclusively by students majoring in computer science. Furthermore, many internships on large software teams don’t involve any communication with those outside the software team. During my 6-month co-op at Amazon Robotics, I can’t point to a single issue in which I had to work deeply with someone with expertise outside of embedded software in order to solve a technical problem. That’s a shame, since communicating across disciplines is a critical skill for solving problems in the real world.&lt;/p&gt;
&lt;p&gt;The other advantage of an experience like the rover team is that it allows you to develop an understanding of fields outside your core strengths. While on rover, I did electrical work, replaced and installed mechanical parts, and even got involved in our filming team for our System Acceptance Review video. My greatest regret on the team is not learning SolidWorks. The base level understanding of electrical and mechanical engineering I developed has come in handy countless times over the years.&lt;/p&gt;
&lt;h1 id=&quot;hands-on-experience&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#hands-on-experience&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Hands-on experience&lt;/h1&gt;
&lt;p&gt;My university is (somewhat) unique in that it encourages students to participate in 6-month “co-ops” instead of 4-month internships. They pride themselves on this “experiential learning” approach, and one could assume that it diminishes the importance of developing hands-on experience through clubs. However, I would argue the opposite: co-op program participants benefit greatly from hands-on club experience at least as much as those on a traditional internship schedule.&lt;/p&gt;
&lt;p&gt;One reason is that club experiences give members a much broader range of fields and topics to study. Co-op projects are often small, self-contained, and confined to a small field and area of the product. In clubs like the rover team, you can choose as many or as few topics as you like to learn about and dive deep into. Clubs like the rover team have countless subsystems and components, both hardware and software, which need to be integrated; with members leaving every four years, there are plenty of opportunities to become the team expert in a particular system.&lt;/p&gt;
&lt;p&gt;During my time on the rover team, I became the “expert” on our command and control interface (since rewriting it happened to be my first project), our radio system (since I was in the right place at the right time to troubleshoot it at my first competition), our camera streaming system (since I arranged to take over development on it after the previous experts graduated), and our migration to ROS 2 (since I was in a leadership position at the time we migrated). Unless you work at an early-stage startup, it’s difficult to develop this breadth of expertise in a corporate environment.&lt;/p&gt;
&lt;p&gt;Similarly, engineering clubs allow members to be part of high-level design decisions and planning steps that are often opaque to interns and co-op students. The design of large-scale software systems is a critical skill often not taught well in classes. You can study design patterns all day long, but putting that knowledge into practice is a skill that takes, well, practice.&lt;/p&gt;
&lt;p&gt;Another advantage of club experiences that it teaches you software development practices &lt;em&gt;before&lt;/em&gt; you go on an internship or co-op. I am always amazed at the number of extremely smart Northeastern students who have no experience with Git, pull request workflows, how to leave a code review, or other critical steps in the software development process until their first co-op. While Northeastern does have a class which covers these, it is taught too late and in too little detail. While you could make the argument that teaching these skills is the purpose of a co-op, it often doesn’t work like that in practice: small companies may employ poor software development practices, large companies like Google or Amazon use their own bespoke workflows unlike others in the industry, and students don’t always get the chance to leave code reviews themselves.&lt;/p&gt;
&lt;h1 id=&quot;leadership-and-collaboration&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#leadership-and-collaboration&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Leadership and collaboration&lt;/h1&gt;
&lt;p&gt;Working on a 4 person team is quite different from working on a 40 person team, and a 40 person team is quite different from a 400 person team. My rover team had around 40 active members at a given time. I think this number is about perfect for a few reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It’s a large enough group of people that a hierarchical team structure is necessary to be productive. Members need to be split into subteams, subteams need leads, and subteam leads need a team lead. No one person can hold all of the expertise about the system in their head at any given time.&lt;/li&gt;
&lt;li&gt;It’s a small enough group of people that becoming the expert in a subsystem or rising into a leadership position is very possible for all incoming members.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’m sure I’m far from the first person to point out that personal projects are no substitute for building things with others, but it’s true! Going directly from personal work to working 40 hours a week on a team is a jarring transition for many. Although software courses try to remedy this with teamwork assignments, there is no substitute for the experience gained working with a large team over multiple years.&lt;/p&gt;
&lt;h1 id=&quot;its-fun&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#its-fun&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;It’s fun!&lt;/h1&gt;
&lt;p&gt;My final reason is that rover development work and competition is fun as hell. If the only software development work I did was for classes or co-op, I worry I would lose interest quickly in the field. Personal projects can be fun to develop as well, but nothing compares to the shared sense of comradery, triumph, and accomplishment I experienced on the rover team.&lt;/p&gt;&lt;/youtube&gt;</content:encoded></item><item><title>Wall Matrix, Take 2</title><link>https://breq.dev/projects/matrix2/</link><guid isPermaLink="true">https://breq.dev/projects/matrix2/</guid><description>A wall-mounted LED matrix display for weather and transit data, take 2!</description><pubDate>Tue, 28 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1912&quot; height=&quot;1912&quot; src=&quot;https://breq.dev/_astro/trains_are_sleeping.Dj_7554t_SXiXf.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;This project is a wall-mounted LED matrix built to show album art, nearby transit and bikeshare information, and weather data.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;Almost four years ago, I made the first iteration of a &lt;a href=&quot;https://breq.dev/projects/wallmatrix&quot;&gt;wall-mounted LED matrix&lt;/a&gt; project to show weather and transit info, and it’s one of the projects I’ve gotten by far the most use out of. It’s been hung at my childhood home in Maine, in various dorm rooms over my college years, and now at my apartment I share with &lt;a href=&quot;https://avasilver.dev/&quot;&gt;Ava&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;&quot;&gt;img]:!aspect-square [&amp;#x26;&gt;img]:!object-cover&quot;&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;713&quot; height=&quot;713&quot; src=&quot;https://breq.dev/_astro/mbta.BxuVeZaS_Rbj7V.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;699&quot; height=&quot;700&quot; src=&quot;https://breq.dev/_astro/weather.kApdxApI_15oMmf.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Over that time, we’ve made only a few changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The display now automatically cycles between weather and transit info (since I like to see both before commuting).&lt;/li&gt;
&lt;li&gt;Weather info now displays in both Fahrenheit and Celsius (since Ava prefers the latter).&lt;/li&gt;
&lt;li&gt;Multiple upcoming trains are shown instead of just one.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, as we’ve added each of these features, we’ve needed to pack more information into a very small (only 16x32!) screen. This tips the balance away from legibility (mostly fine for us, but less useful for guests at our house).&lt;/p&gt;
&lt;p&gt;Simultaneously to this, the two of us got a record player shortly after moving in and have been starting to build a record collection, which has reinvigorated our appreciation of album art. I thought the idea of a piece of wall art to display album covers sounded cool, so I started looking at getting a variant of the LED matrix with a square aspect ratio.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;“Hardware Rev 2” is built around a 64x64 matrix panel with half the pixel pitch of the original, leading to a device with the same width and double the height. I decided to build it with a Raspberry Pi Zero 2W and a &lt;a href=&quot;https://www.adafruit.com/product/3211&quot;&gt;Matrix Bonnet&lt;/a&gt; from Adafruit with matching dimensions to shrink the electronics down from the original and let it sit closer to the wall.&lt;/p&gt;
&lt;p&gt;This also meant that I could build this new revision without taking parts from the old build! I gave the old model to my girlfriend &lt;a href=&quot;https://miakizz.quest&quot;&gt;Mia&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;software&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#software&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Software&lt;/h2&gt;
&lt;p&gt;I also chose to start from scratch with the software of this version. I’ve learned a lot about software development since writing the first iteration of the code, and the old design included way too many layers of abstraction. (Features like the preview display and web server were cool, but saw little day-to-day use.)&lt;/p&gt;
&lt;p&gt;The display supports four main screens with information pulled from Spotify, weather, MBTA, and BlueBikes.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2596&quot; height=&quot;2596&quot; src=&quot;https://breq.dev/_astro/spotify.BqLjI2fP_2rPNka.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The Spotify screen just displays the artwork from whichever album Ava or I am listening to at a given moment. While I was initially happy to see Spotify’s CDN making a perfectly-sized 64x64 version of the image available, it seems to be too affected by JPEG artifacts to be usable here. So, I settle for downscaling a higher-resolution version.&lt;/p&gt;
&lt;p&gt;Spotify data is pulled using the &lt;a href=&quot;https://spotipy.readthedocs.io/en/2.25.0/&quot;&gt;spotipy&lt;/a&gt; library.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2351&quot; height=&quot;2351&quot; src=&quot;https://breq.dev/_astro/weather.DAUOJOPG_1Yt8AY.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The weather screen is pretty similar to the weather screen on the old version, showing an icon from &lt;a href=&quot;https://dhole.github.io/&quot;&gt;Dhole&lt;/a&gt;’s awesome &lt;a href=&quot;https://github.com/Dhole/weather-pixel-icons/&quot;&gt;weather pixel icons&lt;/a&gt; alongside the current time, temperature, and high/low temp for the day. (Thankfully, we now have labels corresponding to the F and C temperatures.) Data is sourced from &lt;a href=&quot;https://openweathermap.org/&quot;&gt;OpenWeatherMap&lt;/a&gt;, using a lookup table I made to match their weather condition codes to weather pixel icons.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2312&quot; height=&quot;2312&quot; src=&quot;https://breq.dev/_astro/mbta.D7DvcsGH_Z1hGfhL.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The MBTA screen is designed to mimic a station countdown clock, but with data pulled from several bus and train lines near my apartment. It merges schedules and realtime predictions from the &lt;a href=&quot;https://api-v3.mbta.com/&quot;&gt;MBTA Realtime API&lt;/a&gt;. I’ve written some custom logic to try to deduplicate schedule and realtime data based on trip identifiers, but it doesn’t always work perfectly.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2200&quot; height=&quot;2200&quot; src=&quot;https://breq.dev/_astro/bikes.D4mHL7p2_t7iKz.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The BlueBikes screen is probably the one I’m most proud of. I hand-drew each of the icons for bikes, e-bikes, and docks that show for each station.&lt;/p&gt;
&lt;p&gt;Bikeshare services publish realtime using the &lt;a href=&quot;https://gbfs.org/&quot;&gt;General Bikeshare Feed Specification&lt;/a&gt; format (GBFS), analogous to the &lt;a href=&quot;https://gtfs.org/&quot;&gt;GTFS&lt;/a&gt; format used for transit data. The BlueBikes API has excellent &lt;a href=&quot;https://bluebikes.com/system-data&quot;&gt;documentation&lt;/a&gt;. It’s a bit inefficient (there’s no way to filter to just a subset of stations that I’ve found yet), but it works great for this use case.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;256&quot; height=&quot;256&quot; src=&quot;https://breq.dev/_astro/fish.DSZFOUmc_Z2nagEB.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;My friends and I have fun with a website called &lt;a href=&quot;http://makea.fish/&quot;&gt;makea.fish&lt;/a&gt;, which generates an image of a fish at 11:11. (We’ve worked on various other &lt;a href=&quot;https://directory.breq.dev/&quot;&gt;fish generators&lt;/a&gt;, too!)&lt;/p&gt;
&lt;p&gt;As a fun easter egg, the LED matrix will switch at 11:11 to showing a fish image generated by makea.fish which refreshes every 10 seconds.&lt;/p&gt;
&lt;h2 id=&quot;hardware&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#hardware&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Hardware&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2931&quot; height=&quot;2931&quot; src=&quot;https://breq.dev/_astro/inside.Dpe3p9X4_1IjQxB.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;A view of the back of the device, showing the 3M command strips used to hang it on the wall&lt;/p&gt;
&lt;p&gt;Just like the original, this LED matrix sign is contained within a 3D-printed enclosure. It uses a Raspberry Pi Zero 2W (my first project with a Pi Zero!) with Adafruit’s &lt;a href=&quot;https://www.adafruit.com/product/3211&quot;&gt;Matrix Bonnet&lt;/a&gt;, using a DC jack “extender” I soldered to route the power input to the bottom of the device.&lt;/p&gt;
&lt;p&gt;The case features a “chin”, since the stackup height of the electronics is annoyingly tall. I also added ventilation holes, since there was a bit of discoloration on the wall from the old design. This was my first 3D printing project in a while (outside of &lt;a href=&quot;https://www.quadratic3d.com/&quot;&gt;my day job&lt;/a&gt; working with volumetric printing).&lt;/p&gt;
&lt;p&gt;Quite a long time ago (was it middle school??), I bought a &lt;a href=&quot;https://www.mpselectmini.com/&quot;&gt;Monoprice Select Mini&lt;/a&gt; 3D printer. It held up surprisingly well over the years, including through two hotend replacements. This is the printer that the old LED sign case was printed with! It had a print volume of only 120x120x120mm, so I had to break up the piece into two separate parts.&lt;/p&gt;
&lt;p&gt;A few years ago, I bought an Ender 3 Pro during a Micro Center sale. (Annoyingly, I was living away from Boston over the summer, so I ended up grabbing it and lugging it around for a full day of sightseeing with my parents — in my defense, the sale was about to expire!) I set it up and ran a few prints, but could seemingly never get it to work as well as my old Monoprice one. While I used this for the &lt;a href=&quot;https://breq.dev/projects/outshine&quot;&gt;rave choker&lt;/a&gt; project, it just didn’t work well for printing larger parts. It spent a few years collecting dust at my parents’ house.&lt;/p&gt;
&lt;p&gt;Then, earlier this year, I decided to take the printer back down to my apartment and try to get it up and running again. I ended up doing almost a full rebuild, and found &lt;em&gt;just two loose screws&lt;/em&gt; holding the Z-axis stepper in place (maybe vibrated out, maybe not tightened properly in the first place). After tightening those, the parts for this project came out beautifully!&lt;/p&gt;
&lt;p&gt;The Ender 3 is large enough to fit the full size of the LED matrix, but isn’t quite big enough to print the entire case in one piece. Thus, I broke the part up into the frame around the LED matrix and the “chin” below which contains the Raspberry Pi. The two parts are attached using heat-set inserts.&lt;/p&gt;
&lt;p&gt;And as for why the parts are pink instead of black: after years of being stored badly, the pink spool was one of the only ones I had which wasn’t made too brittle by the humidity. (Plus, the pink and black scheme is growing on me…)&lt;/p&gt;
&lt;h1 id=&quot;conclusion&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;I’m really happy with this! It fits nicely into our living space and does its job of both looking unique and providing useful info. I have thought of a few improvements to make, which will maybe happen at some point. There’s a lot of empty space in the enclosure right now!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Automatic brightness adjustment&lt;/strong&gt;: The sign easily lights up a dark room. I keep it out in the living room instead of the bedroom, so it’s not a big deal, but it’d still be a nice touch.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;On-device controls&lt;/strong&gt;: It would be nice to be able to configure the device, change modes, turn on/off, etc. with some interface on the device itself. While the display could be reused for an on-screen menu system, we’d need some form of button for user input. It would definitely need to be minimal (I don’t want this to turn into a wall-mounted gameboy). I think an encoder with pushbutton might be a nice touch, since the only thing visible to the outside would be a single knob.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloud interaction&lt;/strong&gt;: The old matrix had a website where anyone could enter a message and it would scroll across the screen. I haven’t added anything like this to the new code (yet), but it’d be pretty fun! (Maybe it could even show drawings using the increased resolution…)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It also feels quite polished compared to a lot of the quick builds I’ve done, like I could do a small run of these. The parts costs are a bit high (mostly the LED matrix itself), so that’ll probably never happen, but I can dream!&lt;/p&gt;</content:encoded></item><item><title>Why Amateur Radio?</title><link>https://breq.dev/2025/01/05/amateur-radio/</link><guid isPermaLink="true">https://breq.dev/2025/01/05/amateur-radio/</guid><description>Hobby radio communication isn&apos;t just for boomers.</description><pubDate>Sun, 05 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The amateur radio hobby is generally dominated by a particular demographic. If you look around at a typical large hamfest, spend any time listening in on a net, or ask anyone for their stories of family members, you’ll easily spot it. As a young queer student, my friends and family members are often surprised to hear that I’m involved in the community. So why do I gravitate to it?&lt;/p&gt;
&lt;h1 id=&quot;projects&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#projects&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Projects&lt;/h1&gt;
&lt;p&gt;As my software knowledge has matured, many of my recent projects have been more focused on building things that I would find useful in my day-to-day life. Amateur radio is a great opportunity for projects like those. Radio hardware is often very open to hacking and experimentation, open-source custom firmware and custom programming tools are extremely common.&lt;/p&gt;
&lt;p&gt;Personally, I enjoy connecting the hobby to my interest in software through projects like &lt;a href=&quot;https://rolodex.breq.dev/&quot;&gt;rolodex&lt;/a&gt;, a “contacts app” for storing callsigns and repeater information, or my work with the &lt;a href=&quot;https://www.northeasternrover.com/&quot;&gt;Northeastern Mars Rover Team&lt;/a&gt; involving autonomous mission control over &lt;a href=&quot;https://en.wikipedia.org/wiki/AX.25&quot;&gt;AX.25&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;adventure-and-practicality&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#adventure-and-practicality&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Adventure and Practicality&lt;/h1&gt;
&lt;p&gt;When amateur enthusiasts talk about “off-grid communication,” it’s often in reference to an emergency scenario in which cellular networks fail. While amateur radio does often prove useful in these scenarios, it’s infrequent enough that I don’t find it particularly exciting.&lt;/p&gt;
&lt;p&gt;Another situation where off-grid communication is desirable is in those few areas which are still unreachable by cell signal. I’ve personally experienced the usefulness of this at the &lt;a href=&quot;https://urc.marssociety.org/&quot;&gt;University Rover Challenge&lt;/a&gt;, where the site has little to no cell coverage and the range of cheap FRS radios is often inadequate. I’ve also tuned my handheld radio to weather frequencies to get forecast information in remote areas of Utah and New Mexico.&lt;/p&gt;
&lt;p&gt;Even in situations where communicating over cell phones is possible, radio can still pose advantages. It’s much easier to communicate via radio while driving a car, while outside with gloves on, or in other situations where reaching for a phone is inconvenient.&lt;/p&gt;
&lt;h1 id=&quot;train-autism&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#train-autism&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Train autism&lt;/h1&gt;
&lt;p&gt;One of my favorite things to use my handheld radio for is scanning: listening to signals on railroad channels, marine channels, and other government or commercial bands. Listening to how radio is used in the world around me illuminates systems and processes like train dispatch, ship-to-ship communication, and more that are otherwise hidden from view. Discovering these frequencies is an interesting and rewarding challenge in and of itself, and seeing these processes firsthand shows so much about the infrastructure that makes our world run.&lt;/p&gt;
&lt;h1 id=&quot;friends&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#friends&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Friends&lt;/h1&gt;
&lt;p&gt;Amateur radio is a hobby more or less predicated on talking to people, so it’s critical to find a group of people you enjoy talking to. Radio communication takes a few forms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;planned contacts between two or more stations (e.g. me talking to my friends)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;nets&lt;/strong&gt;, often hosted on a repeater (for VHF/UHF) or specific frequency (for HF) at a specific time&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;contesting&lt;/strong&gt;, usually on HF, in which stations try to contact as many other stations as possible within a specific timespan (usually a day)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DXing&lt;/strong&gt; (where DX is slang for “distance”), also usually on HF, which usually involves making contacts with distant radio status and can involve digital protocols such as &lt;a href=&quot;https://en.wikipedia.org/wiki/FT8&quot;&gt;FT8&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;HF equipment is more expensive and generally incompatible with apartment life, which means I’m stuck with a handheld for personal use. That said, I’ve done some contesting with &lt;a href=&quot;https://nuwireless.org/&quot;&gt;Northeastern University Wireless Club&lt;/a&gt; and my friend &lt;a href=&quot;https://philo.gay/&quot;&gt;Philo&lt;/a&gt; is an avid FT8 enjoyer — it’s possible!&lt;/p&gt;
&lt;p&gt;Regardless, this leaves me with nets as one of my only options. While I’ve tuned into a few on repeaters around Boston, I generally am not interested in the conversation that goes on there. (I can only take so much listening to boomers talk about their medical problems.)&lt;/p&gt;
&lt;p&gt;Despite how it may seem at first glance, there are a lot of young people in the hobby! Younger radio operators tend to form much more insular groups with their friends or at their university or hackerspace. A good place to spot the younger crowd is often on university-run repeaters — around Boston, good options include &lt;a href=&quot;https://nuwireless.org/&quot;&gt;W1KBN&lt;/a&gt; or &lt;a href=&quot;https://web.mit.edu/w1xm/www/&quot;&gt;W1XM&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If not for my close friends in the hobby, I would likely have nowhere near the level of involvement that I do now. We often use radios as an interesting and unique way to coordinate adventures together. My friend Ari created her own &lt;a href=&quot;https://adryd.com/pages/travel-bandplan/&quot;&gt;bandplan&lt;/a&gt; of simplex frequencies that we often use to communicate.&lt;/p&gt;
&lt;p&gt;Radio can be a daunting hobby to get into due to its social nature. Finding your people is critical to having a good experience — I had a handheld collecting dust on my desk for about a year before I really started using it. The first step in the hobby for many is becoming licensed, which often means working up the courage to show up to an unfamiliar place and take an exam administered by an unfamiliar group of people. Part of the reason I recently became a Volunteer Examiner is to help reduce this barrier to entry among friends, and to give me a better answer when someone I know asks how to get involved.&lt;/p&gt;
&lt;p&gt;As far as hobbies go, amateur radio has a lot to offer, and not fitting the stereotypical demographic shouldn’t hold you back from carving your own niche in the world of radio communications.&lt;/p&gt;</content:encoded></item><item><title>My Home Network Lab Notebook</title><link>https://breq.dev/2024/10/22/home-network/</link><guid isPermaLink="true">https://breq.dev/2024/10/22/home-network/</guid><description>Notes from setting up my homelab.</description><pubDate>Tue, 22 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Some of you might know that &lt;a href=&quot;https://avasilver.dev/&quot;&gt;Ava&lt;/a&gt; and I recently moved to our new home in Somerville! As part of this, I finally got the space to start having fun in building a home network setup.&lt;/p&gt;
&lt;p&gt;While the setup is pretty tame for now (and Ava has specifically banned me from purchasing any rackmount gear — alas…), I hope to keep this post updated with notes I have as I work to build more.&lt;/p&gt;
&lt;h1 id=&quot;core-equipment&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#core-equipment&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Core Equipment&lt;/h1&gt;
&lt;h2 id=&quot;modem&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#modem&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Modem&lt;/h2&gt;
&lt;p&gt;We get internet through a cable provider, so the network starts with a modem. I picked a basic Arris SURFboard with DOCSIS 3.2 support since I wanted the flexibility afforded by running a separate router.&lt;/p&gt;
&lt;p&gt;This modem operates a little strangely: it gives itself &lt;code&gt;192.168.100.1&lt;/code&gt;, hands out &lt;code&gt;192.168.100.10&lt;/code&gt; to the first device it sees on DHCP, then also forwards along the assigned public IP address to the connected device. While waiting for the pfSense router to arrive, we actually had to use a Gl.inet router since neither the modem nor our access point has no routing functionality.&lt;/p&gt;
&lt;h2 id=&quot;router-and-switching&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#router-and-switching&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Router and Switching&lt;/h2&gt;
&lt;p&gt;My router is a small Netgate box running pfSense that was gifted to me by my friend &lt;a href=&quot;https://adryd.com/&quot;&gt;Ari&lt;/a&gt;. It has two LAN ports: one goes to the network switches, and the other goes to the Wi-Fi access point. This lets the router and access point use VLANs to separate out clients on each SSID.&lt;/p&gt;
&lt;p&gt;I keep the travel router I was using in the interim around for any networking shenanigans that might require it.&lt;/p&gt;
&lt;p&gt;I have two Netgear switches I picked up at MIT Swapfest a while ago: one sits near the rest of the equipment on my shelf, and the other sits on my desk to connect to my desktop and oscilloscope. One of these switches was featured in my friend Hunter’s &lt;a href=&quot;https://pixilic.com/devices-of-all-time&quot;&gt;Devices of All Time&lt;/a&gt; blog post, which I somehow didn’t realize until months later :)&lt;/p&gt;
&lt;h2 id=&quot;wi-fi&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#wi-fi&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Wi-Fi&lt;/h2&gt;
&lt;p&gt;After dealing with tons of Wi-Fi dropouts in our old apartment, I decided to invest in a Ubiquiti U6 Mesh access point. It’s definitely overkill, but we get exceptional connection to anywhere in the apartment.&lt;/p&gt;
&lt;p&gt;I was very excited to finally have a router that didn’t look like an over-the-top gaming device. The U6 Mesh is very compact and fits nicely on a shelf — it’s about the size of a tall seltzer can. The PoE power is actually quite useful in a home environment — the cable run is a lot tidier with only a single cable to the device. The only criticism I have so far is that gets pretty warm when it’s running.&lt;/p&gt;
&lt;p&gt;While it’s theoretically possible to set up the device without a UniFi Controller, I couldn’t figure out how to do so (the app kept crashing when I tried to go through the flow). So, I just installed the UniFi server software onto my desktop and ran it so I could configure the device, then closed out of it. The software allows creating any number of separate SSIDs, which we use to run our primary network, an SSID matching my parents’ home in Maine, and an Eduroam hotspot.&lt;/p&gt;
&lt;h1 id=&quot;layer-3&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#layer-3&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Layer 3&lt;/h1&gt;
&lt;p&gt;Our IPv4 subnet runs on &lt;code&gt;10.0.0.0/24&lt;/code&gt;, mostly since it makes it possible to use &lt;a href=&quot;https://en.wikipedia.org/wiki/IPv4#Address_representations&quot;&gt;shorthands&lt;/a&gt; like &lt;code&gt;10.2&lt;/code&gt; instead of &lt;code&gt;10.0.0.2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Our also ISP provides us IPv6 - yay!&lt;/p&gt;
&lt;p&gt;I use pfSense’s Dynamic DNS tools to point &lt;code&gt;home.breq.dev&lt;/code&gt; at our IPv4 address. I couldn’t get this to work for IPv6 (the router would use its own address, where I wanted to direct traffic to another point on the network). So I put our IPv6 prefix directly into the DNS dialog on Cloudflare and hoped for the best.&lt;/p&gt;
&lt;h2 id=&quot;ipv6-addressing&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#ipv6-addressing&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;IPv6 Addressing&lt;/h2&gt;
&lt;p&gt;Getting the “suffix” of my IPv6 address to remain static ended up being a hassle — I wanted my computer to continue to respect the prefix it was given, but to keep the same suffix part instead of randomly generating it.&lt;/p&gt;
&lt;p&gt;I found &lt;a href=&quot;https://man7.org/linux/man-pages/man8/ip-token.8.html&quot;&gt;&lt;code&gt;ip token&lt;/code&gt;&lt;/a&gt; to solve this problem.&lt;/p&gt;
&lt;p&gt;To try things out, I ran:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Accept Router Advertisements to configure the network prefix&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sysctl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-w&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;net.ipv6.conf.enp37s0.accept_ra&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Set an IP token identifier (::bc for my initials)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ip&lt;/span&gt; token &lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; ::bc dev enp37s0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, to make it persistent:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# add net.ipv6.conf.all.accept_ra=1 in the ipv6 section&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;nano&lt;/span&gt; /etc/sysctl.conf

&lt;span class=&quot;token comment&quot;&gt;# NetworkManager equivalent of the ip command&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# My wired network is called &quot;Ethernet&quot;,&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# or by default it&apos;s something like &quot;Wired Connection 1&quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; nmcli connection modify Ethernet ipv6.addr-gen-mode eui64
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; nmcli connection modify Ethernet ipv6.token ::bc
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;split-dns&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#split-dns&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Split DNS&lt;/h2&gt;
&lt;p&gt;For IPv4, since we do NAT, I needed to use Split DNS to make sure services were available both on and off the network. (And since the DNS resolver built into pfSense was synthesizing records for these addresses, I also needed to have it synthesize &lt;code&gt;AAAA&lt;/code&gt; records accordingly too.)&lt;/p&gt;
&lt;h1 id=&quot;services&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#services&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Services&lt;/h1&gt;
&lt;p&gt;We’ve got a few services hosted here, with more potentially to come:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://home.breq.dev/&quot;&gt;home.breq.dev&lt;/a&gt;, largely just a landing page for now hosted with Caddy from my desktop&lt;/li&gt;
&lt;li&gt;a Nextcloud instance with our home files&lt;/li&gt;
&lt;li&gt;the &lt;a href=&quot;https://breq.dev/projects/wallmatrix&quot;&gt;LED matrix&lt;/a&gt;, running through a Cloudflare Tunnel for now&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the future, I’d love to also spin up&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a “home node” for syncing my files with Syncthing&lt;/li&gt;
&lt;li&gt;a permanent place for the UniFi controller software other than my Ubuntu desktop&lt;/li&gt;
&lt;li&gt;a VPN host, for remote maintenance, getting around firewalls, and maybe hooking up my friends’ networks and mine to make a mega home network&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Eventually, I’d love to have a dedicated server for lots of these things (Nextcloud, Syncthing, etc) and maybe a reverse proxy to point to other devices on the network? It’s not a huge priority, but maybe after I upgrade my desktop machine I’ll be able to scrounge together some hardware.&lt;/p&gt;</content:encoded></item><item><title>Make a FiSSH</title><link>https://breq.dev/projects/fissh/</link><guid isPermaLink="true">https://breq.dev/projects/fissh/</guid><description>An SSH-based fish generator that only works at 11:11.</description><pubDate>Wed, 25 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1364&quot; height=&quot;1012&quot; src=&quot;https://breq.dev/_astro/fish.Bi_NQIMP_Z2q7YUd.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;fissh.breq.dev (“make a fissh”) is a terminal-based fish generator that you access via SSH terminal. At 11:11, try running:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;ssh&lt;/span&gt; fissh.breq.dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and you’ll see a fish appear in your terminal!&lt;/p&gt;
&lt;h1 id=&quot;fish-generators&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#fish-generators&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Fish generators&lt;/h1&gt;
&lt;p&gt;My friend &lt;a href=&quot;https://adryd.com/&quot;&gt;Ari&lt;/a&gt; introduced me to the original &lt;a href=&quot;http://makea.fish/&quot;&gt;makea.fish&lt;/a&gt; site (HTTP only), made by &lt;a href=&quot;https://weepingwitch.github.io/&quot;&gt;weepingwitch&lt;/a&gt;. The site embeds an image rendered with PHP showing either a randomly-generated fish or the phrase “come back at 11:11.” Getting the 11:11 fish has become somewhat of a ritual among my friends (we have a Discord channel specifically for posting fish screenshots!)&lt;/p&gt;
&lt;p&gt;Over the last few months, we’ve made a few fun variations on this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://queercomputerclub.ca/projects/quecey-voip/&quot;&gt;A phone number that responds with an SSTV-encoded fish&lt;/a&gt; by &lt;a href=&quot;https://adryd.com/&quot;&gt;adryd&lt;/a&gt; and &lt;a href=&quot;https://www.blackle-mori.com/&quot;&gt;blackle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tris.fyi/dish/&quot;&gt;“22:22 Bake a Dish,” using random Allrecipes photos&lt;/a&gt; by &lt;a href=&quot;https://tris.fyi/&quot;&gt;tris&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fish.lftq.in/&quot;&gt;A rotating Minecraft-style fish&lt;/a&gt; by &lt;a href=&quot;https://lukefelixtaylor.com/&quot;&gt;AlpacaFur&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;“Make a fissh” was written by myself and my girlfriend &lt;a href=&quot;https://avasilver.dev/&quot;&gt;Ava&lt;/a&gt; based on an idea from my girlfriend &lt;a href=&quot;https://miakizz.quest/&quot;&gt;Mia&lt;/a&gt;. It’s our contribution to the weird and wonderful world of fish generators.&lt;/p&gt;
&lt;h1 id=&quot;wish-bubble-tea-and-lip-gloss&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#wish-bubble-tea-and-lip-gloss&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Wish, bubble tea, and lip gloss&lt;/h1&gt;
&lt;p&gt;I got the idea for an SSH-based application from &lt;a href=&quot;https://terminal.shop/&quot;&gt;terminal.shop&lt;/a&gt; (an SSH-based coffee beans store). My first idea was to create a user whose login “shell” is just the fish application, similar to how &lt;a href=&quot;https://git-scm.com/docs/git-shell&quot;&gt;git-shell&lt;/a&gt; only allows running Git commands. However, I couldn’t find a way to make OpenSSH require neither a password nor a public key. I suppose there’s definitely a good reason for that, but it meant I needed to look elsewhere.&lt;/p&gt;
&lt;p&gt;Looking into how terminal.shop implemented their solution, I stumbled upon &lt;a href=&quot;https://github.com/charmbracelet/wish&quot;&gt;Wish&lt;/a&gt;, a Go library designed for “SSH apps.” I decided to build this within the &lt;a href=&quot;https://charm.sh/&quot;&gt;Charm&lt;/a&gt; ecosystem, using Wish, &lt;a href=&quot;https://github.com/charmbracelet/bubbletea&quot;&gt;Bubble Tea&lt;/a&gt; for layout, and &lt;a href=&quot;https://github.com/charmbracelet/lipgloss&quot;&gt;Lip Gloss&lt;/a&gt; for “styling” (i.e., ANSI escape codes).&lt;/p&gt;
&lt;p&gt;This stack imposed some requirements on the project structure: this was probably my first model-view-controller app since taking Fundamentals of CS 2 and I definitely would not have picked Go first. The MVC architecture works fine for something like this (although I would’ve preferred something a bit more component-based / React-y). I found the docs for the Charm projects pretty lackluster, and was often faced with undocumented behavior or limitations that I couldn’t find reference of online. (I probably should’ve realized that the number of GitHub stars probably didn’t correlate to the amount of actual use that the library gets…)&lt;/p&gt;
&lt;p&gt;I won’t pretend to have insightful commentary on Go after using it for a tiny toy project, but I honestly struggle to see myself reaching for it in any situation. My first impression is that it’s the complexity of Rust without any of the error handling, memory safety, or performance benefits, with more than its fair share of strange syntax or stdlib quirks, coupled with tooling that makes strange decisions at times.&lt;/p&gt;
&lt;p&gt;That said, for a toy project, I got a huge amount of learning out of this, so I can’t really complain :)&lt;/p&gt;
&lt;h1 id=&quot;a-fishy-dataset&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#a-fishy-dataset&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;A fishy dataset&lt;/h1&gt;
&lt;p&gt;With the stack out of the way, it’s time to actually build this thing. The most important part of a fish generator is the fish it generates. I decided to go the easy route and source fish ASCII art from &lt;a href=&quot;https://ascii.co.uk/art/fish&quot;&gt;ascii.co.uk&lt;/a&gt;. One thing I like is that most artists leave their initials somewhere in the drawing. I wish I could link to some of the ASCII artists directly from the fish page, but unfortunately, the site doesn’t give any other attribution.&lt;/p&gt;
&lt;h1 id=&quot;finishing-touches&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#finishing-touches&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Finishing touches&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1364&quot; height=&quot;1012&quot; src=&quot;https://breq.dev/_astro/about.DjaA65kG_1xD0o4.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Getting the user’s timezone right is another integral part of fish generation. Web-based generators get this easily from the JavaScript &lt;code&gt;Date&lt;/code&gt; API, but for other protocols, it can take some creativity. The aforementioned SSTV fish telephone line, for instance, just accepts any time ending in &lt;code&gt;:11&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With a terminal, though, we get a bit more information: the user’s IP address. The app uses &lt;a href=&quot;https://ipinfo.io/&quot;&gt;&lt;code&gt;ipinfo.io&lt;/code&gt;&lt;/a&gt; as a geolocation database since their free tier seemed generous and they helpfully provide a &lt;code&gt;timezone&lt;/code&gt; key in their output. As such, the server can ensure each user gets the fish in their corresponding timezone (assuming the GeoIP database is accurate).&lt;/p&gt;
&lt;p&gt;No app would be complete without a short “about” page, let alone one with so many friends to credit. I took advantage of &lt;a href=&quot;https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda&quot;&gt;OSC 8 escape sequences&lt;/a&gt; to embed links to the fish dataset and various folks’ websites. These work in many popular terminal emulators, but not all, and there’s no method to query if a terminal supports OSC 8 and fall back to including full URLs otherwise.&lt;/p&gt;
&lt;h1 id=&quot;deployment&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#deployment&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Deployment&lt;/h1&gt;
&lt;p&gt;Deploying something that runs on port 22 on a server that you presumably also need to SSH into is an annoying situation, and I’ve had enough bad experiences messing up an SSH config and locking myself out of a VPS that I decided to just spin up a new one for this. (Oracle Cloud Free Tier my beloved!) That said, switching OpenSSH to port 2222 and running this on port 22 largely went off without a hitch.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1364&quot; height=&quot;1012&quot; src=&quot;https://breq.dev/_astro/comeback.Cv3x1NhN_VrSHY.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title>Why You Should Write an Emulator</title><link>https://breq.dev/2024/09/14/emulator/</link><guid isPermaLink="true">https://breq.dev/2024/09/14/emulator/</guid><description>How emulator development teaches vital reasoning skills.</description><pubDate>Sat, 14 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One of my favorite quotes is part of &lt;a href=&quot;https://medium.com/@bre/the-cult-of-done-manifesto-724ca1c2ff13&quot;&gt;The Cult of Done Manifesto&lt;/a&gt; by Bre Pettis and Kio Stark:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Those without dirty hands are wrong. Doing something makes you right.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So do it. Go write an emulator. Pick a programming language. Pick a basic CPU architecture: either something retro like the &lt;a href=&quot;https://www.pagetable.com/c64ref/6502/&quot;&gt;6502&lt;/a&gt; or &lt;a href=&quot;https://clrhome.org/table/&quot;&gt;Z80&lt;/a&gt;, or something modern but still simple like the &lt;a href=&quot;https://riscv.org/wp-content/uploads/2017/05/riscv-spec-v2.2.pdf&quot;&gt;RISC-V&lt;/a&gt; base integer instruction set, or something designed for teaching like &lt;a href=&quot;https://github.com/StanWarford/pep9&quot;&gt;Pep/9&lt;/a&gt;. Implement a basic model of its registers and a simple block memory interface. Start with a few opcodes, write some assembly programs to test them out, and then add more.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;By the time I sat down and wrote an emulator, I had taken courses on the architecture of a CPU! And yet writing this one piece of software fundamentally changed my understanding of how computers work at a basic level. Computers are obviously far more complicated than the toy examples I’m suggesting, but advances like branch prediction and caching did not fall out of a coconut tree; they exist in the context of all that came before them.&lt;/p&gt;
&lt;p&gt;Concepts like &lt;a href=&quot;https://www.youtube.com/watch?v=8Dcj19KGKWM&quot;&gt;Return-Oriented Programming&lt;/a&gt; require a strong ability to &lt;em&gt;reason about&lt;/em&gt; how machine code is executed. Working with computers at the lowest level through emulator development helped me develop this reasoning ability far better than a class would.&lt;/p&gt;
&lt;p&gt;Another underrated skill in software development (that I especially think isn’t taught well!) is reading a datasheet and translating that to a software implementation. Writing software in the real world involves working with lots of weird protocols with documentation of varying quality: on Rover I work with tons of random sensors and modules, at work I write code that communicates with lasers with protocols that vary widely between “very reasonable” to “moderately insane,” and in personal projects I frequently find myself reaching to implement communication with a part that lacks a comprehensive library. While implementing my emulator, I reached for datasheets, schematics, and any documentation I could find. And where the docs weren’t clear, I was forced to find ways to answer questions about the protocol experimentally.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;When I started, it felt like almost all resources I found on emulator development tried to talk me out of it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Good knowledge of the chosen language is an absolute necessity for writing a working emulator, as it is quite complex project, and your code should be optimized to run as fast as possible. Computer emulation is definitely &lt;strong&gt;not&lt;/strong&gt; one of the projects on which you learn a programming language.&lt;/p&gt;
&lt;p&gt;- &lt;a href=&quot;http://fms.komkon.org/EMUL8/HOWTO.html&quot;&gt;“How To Write a Computer Emulator” by Marat Fayzullin&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Writing an emulator was my first project in the Rust programming language, and I don’t regret it at all. An emulator forces you to think deeply about your code’s structure: in the real world, retro computers reused chips for different purposes, split functionality across different parts of the system, and were generally full of leaky abstractions. How do you write a program which encapsulates that structure? Mirroring the structure exactly &lt;a href=&quot;https://www.mamedev.org/about.html&quot;&gt;a la MAME&lt;/a&gt; leads to convoluted code, but building too many abstractions leads to quite complex data flow. And if you’re anywhere near as much of a perfectionist as me, you’ll rewrite each component at least three times, agonizing over the small details and using every tool your programming language offers you. And each time you rewrite it, you’ll learn something.&lt;/p&gt;
&lt;p&gt;It is my belief that there is no single project which introduces you to the good, the bad, and the ugly aspects of a programming language than an emulator.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;In classes, computer science assignments are typically &lt;em&gt;rigidly defined&lt;/em&gt; (you’re given an exact set of functionality to implement) and &lt;em&gt;small-scope&lt;/em&gt; (you write code once and never touch it again). There’s great value in just sitting down by yourself a few times a week and building something: not knowing what exactly you’re making nor what “done” even looks like. Being self-guided is an incredibly useful skill: in the real world, you won’t have assignment descriptions or TAs to guide you to a solution, and there’s often no exact description of what a solution looks like. Going from nothing to a functional, complex emulator shows mastery over this skill.&lt;/p&gt;
&lt;p&gt;Large-scope projects are also fundamentally different to work on. While I’m all for building small, toy projects to learn technologies or solve simple problems, they don’t help you learn how to structure a program. While group work can help, I’d make the case that it’s better to learn design patterns in a solo project since you’ll never encounter a pattern you aren’t comfortable with. An emulator is usually a large enough project that you can’t fit the entire thing in your head at once, meaning you’ll need to rely on good design to help you navigate it.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Realistically, the code you’re writing probably looks nothing like an emulator. Maybe you’re doing frontend development, or writing a backend CRUD app, or even doing some embedded work. That said, I hope I’ve made the case for how writing an emulator is an immensely useful exercise regardless of the flavor of software development you do.&lt;/p&gt;</content:encoded></item><item><title>88x31 Dungeon</title><link>https://breq.dev/projects/dungeon/</link><guid isPermaLink="true">https://breq.dev/projects/dungeon/</guid><description>A set of web experiences to traverse the 88x31 graph, inspired by text-based dungeon crawl games.</description><pubDate>Sat, 07 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;br&gt;
&lt;p&gt;88x31 Dungeon is a set of “games” that allow you to traverse the graph of &lt;a href=&quot;https://breq.dev/projects/eightyeightthirtyone&quot;&gt;88x31 buttons&lt;/a&gt;, which are small, pixel art buttons that website owners include to link to related people and projects. It’s based on data collected by &lt;a href=&quot;https://eightyeightthirty.one/&quot;&gt;eightyeightthirty.one&lt;/a&gt;, a scraper which originally mapped the full 88x31 network. The graph is used to generate a “dungeon” grid, with each room containing a particular website. This project can be thought of as an alternate way to visualize and interact with the graph created by eightyeightthirty.one.&lt;/p&gt;
&lt;h1 id=&quot;experiences&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#experiences&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Experiences&lt;/h1&gt;
&lt;p&gt;88x31 Dungeon provides a set of related experiences, each providing a different way to interact with the graph.&lt;/p&gt;
&lt;h2 id=&quot;birds-eye&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#birds-eye&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Birds Eye&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3360&quot; height=&quot;1930&quot; src=&quot;https://breq.dev/_astro/birdseye.D880nnRe_x0AUI.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The birds-eye view shows the full grid and allows the user to scroll across the map. This was the first and simplest experience I implemented, and is useful mostly to visualize the layout of the dungeon. Each square renders the site’s 88x31 and domain name, and it’s possible to “fly” to a specific site.&lt;/p&gt;
&lt;h2 id=&quot;navigate&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#navigate&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Navigate&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3360&quot; height=&quot;1930&quot; src=&quot;https://breq.dev/_astro/navigate.Otad5GSn_ZNc0tt.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Navigate embeds each site with an &lt;code&gt;&amp;#x3C;iframe&gt;&lt;/code&gt; and allows you to visit its neighbors using arrow keys. It’s loosely inspired by the “dungeon crawler” format, but the arrow buttons at the top let you see the domain of the site you’re about to navigate to. It’s almost like a two-dimensional &lt;a href=&quot;https://en.wikipedia.org/wiki/Webring&quot;&gt;webring&lt;/a&gt;. I find it fun to move quickly through this one, getting a feel for each region of the dungeon by seeing each site for a few seconds.&lt;/p&gt;
&lt;h2 id=&quot;walk&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#walk&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Walk&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2338&quot; height=&quot;1674&quot; src=&quot;https://breq.dev/_astro/walk.BZGLA2ig_Z1ICDXt.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Walk presents a draggable canvas on which rooms of the dungeon are embedded as &lt;code&gt;&amp;#x3C;iframe&gt;&lt;/code&gt;s. The buttons at the left allow you to switch between “drag mode” and “interact mode,” where the latter allows you to scroll, click, and interact with each website on the grid. Unlike the other frame-based experiences, Walk shows up to nine frames at once, letting you see each site in the context of its neighbors.&lt;/p&gt;
&lt;p&gt;A limitation of Walk is that each site is rendered in a relatively small viewport, breaking some sites. This is particularly an issue on mobile browsers where the viewport is already very small.&lt;/p&gt;
&lt;h2 id=&quot;crawl&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#crawl&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Crawl&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1610&quot; height=&quot;1866&quot; src=&quot;https://breq.dev/_astro/crawl.B5BIC2Bp_ZWRjps.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Crawl is the most similar experience to a text-based dungeon crawler. At the top, the site is embedded in a 4:3 &lt;code&gt;&amp;#x3C;iframe&gt;&lt;/code&gt;, and at the bottom, the user can run commands in a terminal.&lt;/p&gt;
&lt;p&gt;Crawl provides the following commands:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;go [dir]&lt;/code&gt;: Move in a direction (&lt;code&gt;north&lt;/code&gt;, &lt;code&gt;southeast&lt;/code&gt;, etc)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;look&lt;/code&gt;: Look around (prints the domains of the rooms that border the current room)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fly [domain]&lt;/code&gt;: Teleport to a specific room&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;This project consists of two parts: the code to generate the dungeon, and the frontend experiences.&lt;/p&gt;
&lt;h2 id=&quot;dungeon-layout-generation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#dungeon-layout-generation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Dungeon Layout Generation&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1692&quot; height=&quot;1416&quot; src=&quot;https://breq.dev/_astro/dungeon-full.DHRYjJQT_ZO9AVP.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;A full view of the generated dungeon layout.&lt;/p&gt;
&lt;p&gt;The dungeon is generated using a breadth-first greedy algorithm, which is roughly defined as:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Start by placing &lt;code&gt;breq.dev&lt;/code&gt; at &lt;code&gt;(0, 0)&lt;/code&gt; and adding its neighbor cells to the queue&lt;/li&gt;
&lt;li&gt;Pull a grid cell from the queue&lt;/li&gt;
&lt;li&gt;Find domains with links (in either direction) to the rooms neighboring this cell&lt;/li&gt;
&lt;li&gt;Sort the candidate domains by the number of unique links&lt;/li&gt;
&lt;li&gt;If no candidates exist, return to step 2&lt;/li&gt;
&lt;li&gt;Place the best candidate in a room at this cell&lt;/li&gt;
&lt;li&gt;Add the neighbors of the newly placed cell to the queue&lt;/li&gt;
&lt;li&gt;Return to step 2&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I’m happy with how this algorithm creates neighborhoods, and it places a good portion of the total graph. However, it struggles with cliques, since each node can have at most 4 neighbors.&lt;/p&gt;
&lt;h2 id=&quot;putting-sites-in-frames&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#putting-sites-in-frames&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Putting Sites in Frames&lt;/h2&gt;
&lt;p&gt;Each experience is implemented differently, but most required embedding untrusted sites using &lt;code&gt;&amp;#x3C;iframe&gt;&lt;/code&gt; tags. Iframes are a relic of the old web, and honestly probably would not become a web standard if they were proposed within the last few years. A few issues I ran into included:&lt;/p&gt;
&lt;h3 id=&quot;react-reusing-frame-elements&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#react-reusing-frame-elements&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;React reusing frame elements&lt;/h3&gt;
&lt;p&gt;Previously, some parts of the implementation looked like this:&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot; data-language=&quot;tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;iframe&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;https://&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;room&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domain&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The subtle bug here that creates undesired behavior is that when &lt;code&gt;room&lt;/code&gt; changes, the &lt;code&gt;iframe&lt;/code&gt; element is reused instead of recreated, so the old frame contents persist while the new ones are loaded. This leads to a confusing user experience, especially in Walk where frame positions are constantly changing positions and sources.&lt;/p&gt;
&lt;p&gt;React provides a &lt;code&gt;key&lt;/code&gt; parameter for lists to tell what elements to create, destroy, or reuse. The best solution I found in this case is to create a 1-element list and use this:&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot; data-language=&quot;tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;iframe&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;https://&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;room&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domain&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;room&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domain&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;dead-links-leading-to-frames-not-loading&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#dead-links-leading-to-frames-not-loading&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Dead links leading to frames not loading&lt;/h3&gt;
&lt;p&gt;If you scroll for any appreciable amount of time, you’re likely to find either the “page crashed” icon (in Chrome) or a “Firefox can’t display this page” error (in Firefox). Unfortunately, many targets of 88x31 links are dead pages. While I’d love to show a fallback UI containing the 88x31 button itself in these cases, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#error_and_load_event_behavior&quot;&gt;IFrames don’t send &lt;code&gt;error&lt;/code&gt; events&lt;/a&gt; since it could be used to “probe the URL space of the local network’s HTTP servers.” Since there are many other ways to probe the URL space of the local network’s HTTP servers from a website, I’m not sure what the merit of this policy is, but as web developers we must accept that we live and die at the whim of browser makers.&lt;/p&gt;
&lt;p&gt;Amazingly, &lt;a href=&quot;https://stackoverflow.com/questions/375710/detect-failure-to-load-contents-of-an-iframe/54952975#54952975&quot;&gt;someone found a batshit insane way to solve this problem&lt;/a&gt;, but I’m hesitant to use a brittle solution like this.&lt;/p&gt;
&lt;h3 id=&quot;sites-setting-the-x-frame-options-header&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#sites-setting-the-x-frame-options-header&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Sites setting the &lt;code&gt;X-Frame-Options&lt;/code&gt; header&lt;/h3&gt;
&lt;p&gt;A lot of sites set &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options&quot;&gt;the &lt;code&gt;X-Frame-Options&lt;/code&gt; header&lt;/a&gt; (or its modern alternative, the Content Security Policy directive &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors&quot;&gt;&lt;code&gt;frame-ancestors&lt;/code&gt;&lt;/a&gt;) to prevent untrusted websites from loading them as frames.&lt;/p&gt;
&lt;p&gt;There is a legitimate security argument for some sites due to an attack called &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/Clickjacking&quot;&gt;Clickjacking&lt;/a&gt;, in which trusted content is loaded into an &lt;code&gt;&amp;#x3C;iframe&gt;&lt;/code&gt; and buttons, input fields, or other interactive elements are overlaid on top of the frame to capture input. However, this isn’t applicable to most simple personal websites.&lt;/p&gt;
&lt;p&gt;While it might be possible to build and use a proxy which strips these headers from the site, doing so would violate the spirit of this header: if someone set this header, they would probably prefer their site not be embedded into other sites, and I don’t want to override their preference. (I’d still really prefer to show a fallback… alas.)&lt;/p&gt;
&lt;h3 id=&quot;sandboxing-frames&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#sandboxing-frames&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Sandboxing frames&lt;/h3&gt;
&lt;p&gt;During early development, I found a few sites acting annoyingly, including one that would navigate the top-level window away (what the hell?). While I’d love it if everyone on the indie web played nice, I did implement some protections using &lt;a href=&quot;https://web.dev/articles/sandboxed-iframes&quot;&gt;the &lt;code&gt;sandbox&lt;/code&gt; attribute&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I added the following flags:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;allow-scripts&lt;/code&gt;, since JS in frames is mostly harmless&lt;/li&gt;
&lt;li&gt;&lt;code&gt;allow-same-origin&lt;/code&gt;, since leaving this out interferes with many websites’ ability to load fonts and other resources&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This still lets frames cause problems (lagging the user’s computer, autoplaying annoying audio, etc), but the worst of the problems have been dealt with. Plus, this project deciding what’s annoying and restricting features of iframes runs counter to the spirit of showcasing the variety present in the indie web. As such, this approach strikes a balance between maintaining the availability of the site and allowing for creative expression.&lt;/p&gt;
&lt;h1 id=&quot;conclusion&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;While some parts are a little clunky, the intent behind this was less a polished end result and more an experiment into designing interesting and fun experiences which are part game and part visualization tool. I’m happy with the variety present in the end result. Undoubtedly I think I’m going to end up finding more ideas for experiences, and I’m excited to explore this idea further.&lt;/p&gt;</content:encoded></item><item><title>Thoughts on Cities, Navigation, and Home</title><link>https://breq.dev/2024/08/15/boston/</link><guid isPermaLink="true">https://breq.dev/2024/08/15/boston/</guid><description>Or: The Unreasonable Effectiveness of Biking in Boston</description><pubDate>Thu, 15 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;To paraphrase a close friend of mine:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Boston is like a Grand Theft Auto map. There’s a lot to do, but it never really feels all that spread out.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I moved to Boston almost three years ago from today. What I love most about this city is how accessible it is. Here are, roughly chronologically, the ways I’ve picked up to navigate here, and my reflections on city life.&lt;/p&gt;
&lt;h1 id=&quot;the-subway&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-subway&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Subway&lt;/h1&gt;
&lt;p&gt;By far the most obvious answer to “how do you get around?” is the subway. Despite distinctly remembering a CharlieCard shortage for around a month after I arrived in the city, I quickly started using the system to explore.&lt;/p&gt;
&lt;p&gt;The subway gave me direct access to downtown, and somewhat efficient access to many other places around the city. But using it also feels kind of like “fast travel” in a video game: you hit a button somewhere, wait for the map to load in, and boom, you’re somewhere else, bypassing all of the beautiful level design in between.&lt;/p&gt;
&lt;h1 id=&quot;walking&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#walking&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Walking&lt;/h1&gt;
&lt;p&gt;When I first moved here (and didn’t have many friends), I would sometimes pass the time by just stepping outside, pointing myself in a random direction, and walking. When I would get tired, I would start looking for a T stop or just turn around and head home.&lt;/p&gt;
&lt;p&gt;Shortly after, I started challenging myself to walk places without looking at any map: learning street names, orienting myself based on the skyscrapers and cranes overhead, and taking plenty of wrong turns in the process.&lt;/p&gt;
&lt;p&gt;I love the chaotic street network of Boston, and how it always gives the sense that there’s more to explore.&lt;/p&gt;
&lt;p&gt;Walking puts me on the same scale as the world around me. I can look into any storefront, stop to peek down side streets, or take in the scenery around me. To this day I’ll still happily walk impractical distances from time to time for no reason other than to take in my surroundings.&lt;/p&gt;
&lt;h1 id=&quot;the-bus&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-bus&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Bus&lt;/h1&gt;
&lt;p&gt;It took me about a year before I started taking MBTA buses. Many friends I’ve talked to share the same experience. I’m not sure if it’s because the bus network is harder to understand, buses are the more “looked down upon” form of transit, or if those who are new to city life, like my past self, are just less aware of the utility of local bus routes.&lt;/p&gt;
&lt;p&gt;I’ve found the social dynamics on buses to be quite different to the subway. You’ll see tons of people piling into the back doors on the Green Line to avoid paying the fare, but you almost never see that on buses. On the bus, there’s a much stronger sense of “we’re all in this together.”&lt;/p&gt;
&lt;p&gt;Buses also let me expand my radius of places I can explore. A few of my friends live in places vaguely accessible by infrequent bus routes, and while we’ll usually opt for the more practical option of meeting at a subway line terminus, it’s fun to occasionally take a long, winding transit journey.&lt;/p&gt;
&lt;h1 id=&quot;commuter-rail&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#commuter-rail&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Commuter Rail&lt;/h1&gt;
&lt;p&gt;While the most expensive transit mode on this list, the commuter rail is hugely useful both for going beyond the confines of the city and quickly getting to and from downtown (provided you time it right). Honestly, it’s one of the modes I wish I took advantage of more.&lt;/p&gt;
&lt;p&gt;Also: the $10 weekend passes are an insanely good deal. Use them.&lt;/p&gt;
&lt;h1 id=&quot;bluebikes&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#bluebikes&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;BlueBikes&lt;/h1&gt;
&lt;p&gt;I didn’t start biking in the city until recently, but I immediately fell in love with it. Biking immediately felt like the “just right” scale: when riding, you can see every detail of every street, you have full autonomy to stop and explore and detour to your heart’s content, but you also get places &lt;em&gt;fast&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;When I was young, I used to watch those Casey Neistat vlogs where he lane-splits through NYC traffic on a skateboard, and I always wished I could experience a feeling like that. Biking gives me that excitement: navigating complex situations at high speed, finding miniscule ways to shave time off of trips, and playing my small part in the chaotic yet coordinated motion of traffic.&lt;/p&gt;
&lt;p&gt;I expected biking in Boston to be scary, mostly because people tend to make it out to be. But coming from narrow, rural, two-lane roads with large trucks whizzing by at highway speeds, the city feels like a cakewalk by comparison. Sure, you’re navigating way more interactions with cars than you would be otherwise, but you don’t get nearly the speed differentials of somewhere more rural.&lt;/p&gt;
&lt;p&gt;That said, if you take anything away from this post, wear a helmet. One of my most “I can’t believe I didn’t buy this sooner” purchases was a nice folding helmet that fits in my backpack, so I’m ready to ride safely whenever.&lt;/p&gt;
&lt;h1 id=&quot;cars&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#cars&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Cars&lt;/h1&gt;
&lt;p&gt;My girlfriend and I are looking to buy a car soon. While I’m excited to have easier access to places beyond the city, I’m simultaneously nervous that the convenience of car trips will make me lose out on the connection to the city that life without a car has allowed me to build over the past three years.&lt;/p&gt;
&lt;p&gt;To end on an anecdote: Once, after a robotics competition in rural Utah, a close friend of mine picked me up to drive me to her home in New Mexico. Sitting on the center console was a notebook on which she had written the numbers: 70, 191, 491, 160, 84 — the highway numbers we followed on our drive back.&lt;/p&gt;
&lt;p&gt;I asked about the notebook, and she said it she was trying to use Google Maps less since she felt it made her less connected to the areas she drove through: instead of looking out the window to understand and be present in the places you drive through, it encourages you to just follow the arrows, ignoring the world around you.&lt;/p&gt;
&lt;p&gt;Whenever I navigate a place by memory or intuition, be that the chaotic streets of Boston, the sprawling MBTA bus network, the back roads of small towns in Maine, or the Albuquerque airport I hadn’t been through in a year, I feel, in a way, welcomed by the communities I pass through. I feel nostalgic for the times I passed through those same places before. I feel excited to explore, knowing that wrong turns can be less of an annoyance, more of an adventure. I feel grounded, able to anchor my experiences to the world happening around me as I traverse through it. Above all, I feel content, no longer stressed by the arrow on my phone screen, just present in the here and now.&lt;/p&gt;</content:encoded></item><item><title>Rolodex</title><link>https://breq.dev/projects/rolodex/</link><guid isPermaLink="true">https://breq.dev/projects/rolodex/</guid><description>A &quot;contacts app&quot; for amateur radio callsigns and DMR IDs.</description><pubDate>Wed, 03 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2226&quot; height=&quot;1434&quot; src=&quot;https://breq.dev/_astro/mine.uu5Osm9R_Zdjaed.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;My own card as it appears in my account.&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;Rolodex stores a virtual contact card with a callsign, name, and other metadata for each of your amateur radio friends. Cards can be viewed, created, and updated on a desktop or mobile device.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;My friends and I often use local amateur radio repeaters to communicate, so I often need to call one of them by callsign. I didn’t feel like I had a centralized place to store this information: a text note would be difficult to navigate and annoying to sync between devices, a traditional contacts app lacks an appropriate field for callsign or DMR information, and I wanted something that I could search by callsign in case I can’t quite remember who holds a particular call. The resulting app is a single place where I can track all of my friends’ calls, and is something I can easily reference from my phone in the field.&lt;/p&gt;
&lt;p&gt;I chose the name “Rolodex” because it conveyed the purpose of the app (storing contacts), and it sounded unique, functional, and a little playful.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The frontend uses the stack I tend to reach for for frontend work: Vite, TypeScript, React, and Tailwind.&lt;/p&gt;
&lt;p&gt;Cards use a fixed aspect ratio to follow the &lt;a href=&quot;https://en.wikipedia.org/wiki/ISO/IEC_7810#ID-1&quot;&gt;ID-1&lt;/a&gt; spec. I used the morse code version of the callsign as an artsy divider between the callsign and name — this uses a specific morse code font file. The interface uses a mix of DIN and JetBrains Mono, two fonts I haven’t worked with much before, since I felt they worked together well and gave each card a utilitarian look without being off-putting.&lt;/p&gt;
&lt;p&gt;The “rolling” animation in the column view works by checking where each card is in the scroll view with a JavaScript event, then applying rotation in X and translation in Y and Z. This took me the most effort to get right, and I’m probably going to continue tweaking it to improve its behavior across various screen sizes.&lt;/p&gt;
&lt;p&gt;The backend of the app is entirely done in Firebase. This was my first Firebase project, and I figured it was a good fit since the requirements are quite standard (it needs a simple authentication system and a basic way for each user to store and retrieve a small amount of data).&lt;/p&gt;
&lt;p&gt;The app is a Progressive Web App, allowing me to install it on the home screen of my phone.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;You can try it at &lt;a href=&quot;https://rolodex.breq.dev/&quot;&gt;rolodex.breq.dev&lt;/a&gt;! At the time of writing, we have 5 total users, including myself! Personally I have more than a dozen contacts saved already, and am constantly adding more. I’ve already found it quite useful, and am looking forward to using it more as more of my friends get licensed and have callsigns issued to them.&lt;/p&gt;</content:encoded></item><item><title>My Dream Handheld Computer</title><link>https://breq.dev/2024/06/21/handheld/</link><guid isPermaLink="true">https://breq.dev/2024/06/21/handheld/</guid><description>Nostalgia for the PocketCHIP, and my dream for a handheld tinkering device</description><pubDate>Fri, 21 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;nostalgia&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#nostalgia&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Nostalgia&lt;/h1&gt;
&lt;p&gt;When I was younger, I had a &lt;a href=&quot;https://www.theverge.com/circuitbreaker/2016/7/19/12227806/pocketchip-review-portable-linux-computer&quot;&gt;PocketCHIP&lt;/a&gt; handheld that I would carry around. It’s what I wrote the &lt;a href=&quot;https://breq.dev/projects/makergamer&quot;&gt;MakerGamer&lt;/a&gt; fantasy console for, and it’s the device I first started experimenting with &lt;a href=&quot;https://www.open-mesh.org/projects/batman-adv/wiki/Doc-overview&quot;&gt;batman-adv&lt;/a&gt; mesh networking on.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;530&quot; height=&quot;942&quot; src=&quot;https://breq.dev/_astro/makergamer.CpJMcQTF_rFMbq.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I loved being able to have a Linux box with me that I could use for experimentation. The keyboard sucked, and the interface wasn’t amazing on a tiny-resolution touchscreen, but that didn’t matter for quick usage, and it was still miles ahead of pulling up Termux on my phone.&lt;/p&gt;
&lt;p&gt;Part of what got me thinking about this is seeing a few of my friends pick up &lt;a href=&quot;https://flipperzero.one/&quot;&gt;Flipper Zero&lt;/a&gt; devices. These have that quality of being a device for “real-world” tinkering and exploration, encouraging people to interact with systems in the world around them. The main reason I don’t have one is just that I’m not particularly interested in RFID/NFC applications (but I applaud those who are).&lt;/p&gt;
&lt;p&gt;About a year ago I picked up a cheap &lt;a href=&quot;https://www.baofengradio.com/products/uv-5r&quot;&gt;Baofeng&lt;/a&gt; handheld radio for amateur use. While it’s a practical way to communicate with friends on a short range, I’ve found the most fun part is using it to &lt;em&gt;explore the world around me&lt;/em&gt; — finding repeaters and trying to communicate with them, listening on railroad or marine channels, and generally exploring to see what systems out there that I can observe. (See &lt;a href=&quot;https://breq.dev/projects/codeplug&quot;&gt;codeplug&lt;/a&gt; for how I’ve iterated on this process over time).&lt;/p&gt;
&lt;p&gt;I want a device which combines the convenience of a Baofeng, the customizability of a Flipper, and the power and peripherals of a PocketCHIP. But more broadly, I want something that encourages that sort of exploration with a physical element to it.&lt;/p&gt;
&lt;p&gt;There was a time recently when I was sitting out in a park downtown, poking at some interesting stuff accessible over Wi-Fi that I’m not quite ready to talk publicly about yet. While I was doing this on my laptop, doing so felt odd given all I needed was a basic environment where I could configure my Wi-Fi settings and invoke SSH. A dedicated handheld device would have been just as functional and a bit more fun.&lt;/p&gt;
&lt;h1 id=&quot;my-dream-handheld-computer&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#my-dream-handheld-computer&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;My Dream Handheld Computer&lt;/h1&gt;
&lt;p&gt;Here’s a rough list of requirements that I’d look for in such a device. This is based on my own interests and the areas of tech I tend to explore most.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Main Processor:&lt;/strong&gt; I want something that runs a proper Desktop Linux distro (not Busybox etc), and is powerful enough to run at least a basic Web browser. This probably means a Raspberry Pi compute module or similar.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Battery:&lt;/strong&gt; At least enough for a full day of running around and using it intermittently.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keyboard:&lt;/strong&gt; Full QWERTY. Good enough to use the CLI with.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Screen:&lt;/strong&gt; This doesn’t need to be good enough to display something like GNOME well, but should at least allow for basic GUI apps and comfortable terminal use.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hardware Connections:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;A full RJ45 Gigabit Ethernet port. I’ve had so many cases where I just want to get on a network quickly and run an SSH command, and breaking out my laptop plus dongle and messing with the macOS network settings is always painfully clunky.&lt;/li&gt;
&lt;li&gt;USB-C (as an Ethernet gadget, etc). For longer development sessions, I’ll want to connect a proper laptop.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Radios:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Wi-Fi is a necessity in today’s world, Bluetooth would be a nice-to-have.&lt;/li&gt;
&lt;li&gt;NFC capabilities would be cool for people who are into that sort of thing.&lt;/li&gt;
&lt;li&gt;I’d love some sort of amateur or otherwise “unconventional” radio capabilities, to allow these devices to be used for communication. While drop-in modules for the 2m band are more rare, lots of them exist for the 70cm (430 MHz, licensed) and 33cm (900 MHz, unlicensed) bands. 430 MHz would likely give better range/performance, while 900 MHz gives interoperability with the &lt;a href=&quot;https://meshtastic.org/&quot;&gt;Meshtastic&lt;/a&gt; network.&lt;/li&gt;
&lt;li&gt;I’d also like a proper external antenna for the above radio, like you’d find on an amateur HT.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extensibility:&lt;/strong&gt; Adding external exposed GPIOs would be cool, but also, I haven’t seen many people taking advantage of them with the Flipper Zero or PocketCHIP. Connectors like &lt;a href=&quot;https://learn.adafruit.com/introducing-adafruit-stemma-qt&quot;&gt;Adafruit STEMMA&lt;/a&gt; might be more useful in practice.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;existing-products&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#existing-products&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Existing Products&lt;/h1&gt;
&lt;h2 id=&quot;dead-stock-pocketchip&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#dead-stock-pocketchip&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Dead Stock PocketCHIP&lt;/h2&gt;
&lt;p&gt;Up until recently, the website &lt;a href=&quot;https://shop.pocketchip.co/&quot;&gt;pocketchip.co&lt;/a&gt; has sold old PocketCHIPs (and still sells CHIPs, albeit at 4x the original selling price). Even if they were still available, though, the lack of up-to-date software for them makes common tasks like flashing a hassle.&lt;/p&gt;
&lt;h2 id=&quot;clockwork-uconsole&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#clockwork-uconsole&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Clockwork uConsole&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.clockworkpi.com/uconsole&quot;&gt;Clockwork uConsole&lt;/a&gt; is probably the closest thing I’ve found to the device I want, but since it’s designed more for &lt;a href=&quot;https://en.wikipedia.org/wiki/Fantasy_video_game_console&quot;&gt;“fantasy console”&lt;/a&gt; use, it falls short in a few ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Available ports are limited. While it has a USB port for host and device use, it lacks a physical Ethernet port :(&lt;/li&gt;
&lt;li&gt;It features an extension module interface, but currently the only such module is an LTE modem (probably not that useful to me since I could just use my smartphone hotspot). Maybe a module for something like LoRa could be designed using the same connector, with an external antenna poking the side?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s also still limited to pre-order.&lt;/p&gt;
&lt;h2 id=&quot;whatever-arturo182-is-cooking&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#whatever-arturo182-is-cooking&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Whatever arturo182 Is Cooking&lt;/h2&gt;
&lt;tweet id=&quot;1765861889798459809&quot;&gt;
&lt;p&gt;Solder Party has previously sold a &lt;a href=&quot;https://www.tindie.com/products/arturo182/keyboard-featherwing-qwerty-keyboard-26-lcd/&quot;&gt;“Keyboard FeatherWing”&lt;/a&gt; containing an LCD, QWERTY keyboard of the type you’d find on a Blackberry phone, and a place for an &lt;a href=&quot;https://learn.adafruit.com/adafruit-feather/overview&quot;&gt;Adafruit Feather&lt;/a&gt; board. While a Feather doesn’t provide quite the amount of computing power I’d like for such a device, I love the form factor and the pricing is reasonable.&lt;/p&gt;
&lt;h2 id=&quot;rooted-android-devices&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#rooted-android-devices&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Rooted Android Devices&lt;/h2&gt;
&lt;p&gt;What about carrying around rooted Android phone for something like this, or rooting my daily driver? This would give an unrestricted Linux environment to play around with, but it also falls short of what I’m after:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Poor connectivity: no RJ45, spotty USB host support.&lt;/li&gt;
&lt;li&gt;Not quite “Desktop Linux-y” enough to run apps designed for typical Linux distros.&lt;/li&gt;
&lt;li&gt;Using a CLI with a touchscreen keyboard is painful.&lt;/li&gt;
&lt;li&gt;No real extensibility for something like LoRa.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;designing-my-own&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#designing-my-own&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Designing My Own?&lt;/h2&gt;
&lt;p&gt;While I’m relatively comfortable with basic PCB design, this is something that’s likely way beyond my skill level. Plus, I’d ideally like to have something that can have a community form around it (like PocketCHIP did, or like Flipper Zero does now). There’s also the enclosure to consider — PocketCHIP did this relatively well and the Flipper does it perfectly.&lt;/p&gt;
&lt;p&gt;I’m largely ruling this out unless I suddenly meet lots of people who share this vision but have much stronger electrical/mechanical skills than I do.&lt;/p&gt;
&lt;h2 id=&quot;something-else&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#something-else&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Something Else?&lt;/h2&gt;
&lt;p&gt;If you can think of something that fits in this general category/vibe of product, please let me know about it! Even if it isn’t what I’m personally looking for, I really want to learn more about projects in this space.&lt;/p&gt;&lt;/tweet&gt;</content:encoded></item><item><title>Codeplug</title><link>https://breq.dev/projects/codeplug/</link><guid isPermaLink="true">https://breq.dev/projects/codeplug/</guid><description>Automatically generate radio configurations from a crowdsourced list of repeaters and channels.</description><pubDate>Thu, 13 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1644&quot; height=&quot;1312&quot; src=&quot;https://breq.dev/_astro/codeplug.BwbUZ-tR_Z1nHmsw.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;The codeplug generation tool in action.&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;Codeplug is a tool to automatically generate a configuration file (or colloquially, a “codeplug”) for a handheld amateur radio.&lt;/p&gt;
&lt;p&gt;The tool prompts users to select their geographic regions of interest and to choose which common simplex channels on amateur and other bands to include (such as calling frequencies, FRS channels, etc.), and builds a &lt;code&gt;.csv&lt;/code&gt; file containing those channels and channels for radio repeaters in their selected regions.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;Many of my friends are licensed amateur radio operators, and when we travel together, we often exchange notes on which repeaters to program into our radios. However, as I’ve made more extensive use of my radio, my previous strategy of including every repeater I can think of in my codeplug has caused me to hit the channel limit.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://adryd.com/&quot;&gt;Ari&lt;/a&gt; and I had the idea for a program which can automatically generate a suitable codeplug based on a subset of regions, channels, etc., allowing us to easily program in relevant channels to our radio based on our current interests, upcoming trips, and more.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The tool is a CLI app built with &lt;a href=&quot;https://python-inquirer.readthedocs.io/en/latest/index.html&quot;&gt;Inquirer&lt;/a&gt; for Python. It loads repeater and channel definitions from &lt;code&gt;.yaml&lt;/code&gt; and &lt;code&gt;.csv&lt;/code&gt; files, and assembles a &lt;code&gt;.csv&lt;/code&gt; which can be imported into &lt;a href=&quot;https://chirpmyradio.com/projects/chirp/wiki/Home&quot;&gt;CHIRP&lt;/a&gt;, a common tool for editing and uploading these configs.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;Instead of continuing to maintain my codeplug &lt;a href=&quot;https://github.com/breqdev/chirp&quot;&gt;by hand&lt;/a&gt;, I’ll be using and improving this tool going forward. A few of my friends have already offered to contribute repeater information to it, and I’m planning on extending it to include &lt;a href=&quot;https://www.on-track-on-line.com/scanner-radio.shtml&quot;&gt;AAR channels&lt;/a&gt; by region as well.&lt;/p&gt;</content:encoded></item><item><title>DDS Tuning for ROS 2</title><link>https://breq.dev/2024/05/17/dds/</link><guid isPermaLink="true">https://breq.dev/2024/05/17/dds/</guid><description>Optimizing RTPS flows such as ROS 2 traffic over lossy networks.</description><pubDate>Fri, 17 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Picture this: you’re developing a robot that you aim to control using a nice, clean user interface. Control goes through this UI on one laptop over a network connection to the robot, which runs its own SBC, all using standard ROS 2. You do a ton of testing, at first just with an Ethernet cable since you don’t feel like dragging your radios out of the closet, and everything looks good! And then, you show up to an event with your robot, and you realize that whenever you switch views in the UI, suddenly your entire comms link grinds to a halt.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;771&quot; height=&quot;254&quot; src=&quot;https://breq.dev/_astro/traffic-spike.B5zb3RQY_ZmuLC4.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;So, what gives?&lt;/p&gt;
&lt;h1 id=&quot;discovery-traffic-and-fragmentation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#discovery-traffic-and-fragmentation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Discovery Traffic and Fragmentation&lt;/h1&gt;
&lt;p&gt;This issue is a classic case of discovery traffic trashing your link. Whenever the UI subscribes to a new topic in ROS, the Data Distribution Service (DDS) middleware needs to figure out which node in the network is publishing that topic, so it generates a ton of multicast UDP traffic to send out to the network. This works fine over a high-bandwidth Ethernet link, but causes problems with a bandwidth-constrained radio connection.&lt;/p&gt;
&lt;p&gt;When our team hit this, we took some Wireshark captures before and after. Here’s the normal, working state of the network:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1123&quot; height=&quot;479&quot; src=&quot;https://breq.dev/_astro/wireshark-good.C47XQLdx_Z29FiNR.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;And here’s after we generate a bit of DDS discovery traffic:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1123&quot; height=&quot;955&quot; src=&quot;https://breq.dev/_astro/wireshark-bad.Cu6tHnYL_1bhgEj.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The unassembled fragments! The horror…&lt;/p&gt;
&lt;p&gt;If you’re not familiar with fragmentation, it’s something that happens at the network layer to break large packets into smaller ones to fit your particular network’s maximum transmission unit (MTU), which is usually 1500. This disassembly and reassembly is typically transparent to the user, and is handled at the OS level. The fact that Wireshark is showing these unassembled fragments implies that they couldn’t be reassembled correctly — some of the fragments were dropped, and now the message can’t be recovered.&lt;/p&gt;
&lt;p&gt;In other words, this is &lt;em&gt;not&lt;/em&gt; cute, computer networks only do this when they’re in &lt;em&gt;extreme&lt;/em&gt; distress.&lt;/p&gt;
&lt;h2 id=&quot;tunable-parameters&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#tunable-parameters&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Tunable Parameters&lt;/h2&gt;
&lt;p&gt;The ROS 2 documentation &lt;a href=&quot;https://docs.ros.org/en/rolling/How-To-Guides/DDS-tuning.html&quot;&gt;suggests&lt;/a&gt; a few knobs which you can turn to hopefully improve performance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reduce &lt;code&gt;net.ipv4.ipfrag_time&lt;/code&gt;.&lt;/strong&gt; By default, Linux allows 30 seconds for a packet to be reassembled before giving up and dropping the fragments from memory. If a lot of fragmentation is happening, allowing this much time can fill buffers quickly, and on a small network (on the scale of one robot and its base station), it shouldn’t ever take more than a few seconds for a fragment to get from one end to the other. ROS recommends a value of 3 seconds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Increase &lt;code&gt;net.ipv4.ipfrag_high_thresh&lt;/code&gt;.&lt;/strong&gt; This controls the amount of memory used for packet defragmentation. By default it’s 256 KiB, but in a scenario with lots of fragmentation happening, you can bump this up as high as 128 MB.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Increase &lt;code&gt;net.core.rmem_max&lt;/code&gt;.&lt;/strong&gt; This controls the size of the buffer that the Linux kernel uses for receiving data on a socket. ROS suggests anywhere from 4 MiB to 2 GiB depending on the DDS vendor.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;alternative-dds-middleware&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#alternative-dds-middleware&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Alternative DDS Middleware&lt;/h2&gt;
&lt;p&gt;One thing to try is changing the actual DDS implementation you’re using. DDS is an open standard, and there are plenty of implementations to choose from. The popular implementations all have corresponding ROS middlewares written, and it’s trivial to switch from one to the other.&lt;/p&gt;
&lt;p&gt;The default middlewares so far have been:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ardent, Bouncy, Crystal, Dashing, Eloquent, Foxy: &lt;strong&gt;FastDDS&lt;/strong&gt; by eProsima&lt;/li&gt;
&lt;li&gt;Galactic: &lt;strong&gt;CycloneDDS&lt;/strong&gt; by Eclipse&lt;/li&gt;
&lt;li&gt;Humble, Jazzy: &lt;strong&gt;FastDDS&lt;/strong&gt; by eProsima&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition to FastDDS and CycloneDDS, there are ConnextDDS and GurumDDS, but those lack open-source licenses.&lt;/p&gt;
&lt;p&gt;Interestingly, Cyclone is seemingly more loved by the community than FastDDS: The MoveIt inverse kinematics tool &lt;a href=&quot;https://moveit.ros.org/install-moveit2/binary/&quot;&gt;recommends Cyclone&lt;/a&gt;. For what it’s worth, switching to Cyclone largely fixed our discovery traffic problems:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;761&quot; height=&quot;226&quot; src=&quot;https://breq.dev/_astro/cyclonedds.Ce5zE4ee_1sklat.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;dds-overhead&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#dds-overhead&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;DDS Overhead&lt;/h1&gt;
&lt;p&gt;This &lt;a href=&quot;https://cdck-file-uploads-global.s3.dualstack.us-west-2.amazonaws.com/business7/uploads/ros/original/2X/7/76acdd6b89e8faf4b3ab63cd170d7b4fc6bd0924.pdf&quot;&gt;slide deck&lt;/a&gt; by Charles Cross goes into detail about some techniques for optimizing ROS 2 traffic. One often-overlooked feature of networking in ROS 2 is the amount of overhead for each message.&lt;/p&gt;
&lt;p&gt;Here’s a packet containing a command that we sent to the left wheel of our robot. It’s a single 32-bit floating point number, so the value itself takes up 4 bytes. How much overhead does transmitting this incur?&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3242&quot; height=&quot;1878&quot; src=&quot;https://breq.dev/_astro/wireshark-packet.BUCJPY90_ZNsfcQ.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;That’s &lt;strong&gt;138&lt;/strong&gt; bytes over the wire:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;14 bytes of Ethernet header&lt;/li&gt;
&lt;li&gt;20 bytes of IPv4 header&lt;/li&gt;
&lt;li&gt;8 bytes of UDP header&lt;/li&gt;
&lt;li&gt;96 bytes of RTSP data
&lt;ul&gt;
&lt;li&gt;20 bytes of RTSP header&lt;/li&gt;
&lt;li&gt;12 bytes of &lt;code&gt;INFO_TS&lt;/code&gt; submessage&lt;/li&gt;
&lt;li&gt;32 bytes of &lt;code&gt;DATA&lt;/code&gt; submessage
&lt;ul&gt;
&lt;li&gt;24 bits of header&lt;/li&gt;
&lt;li&gt;4 bytes of encapsulation kind/options&lt;/li&gt;
&lt;li&gt;&lt;em&gt;4 bytes of data (this is our actual message)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;32 bytes of &lt;code&gt;HEARTBEAT&lt;/code&gt; submessage&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In other words, sending each message requires &lt;em&gt;132 bytes of overhead&lt;/em&gt;, regardless of the actual size of the message. With this in mind, we can greatly reduce the load over the network by combining as much as possible into as few topics as possible. In practice, here’s how we applied this:&lt;/p&gt;
&lt;h2 id=&quot;custom-messages&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#custom-messages&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Custom Messages&lt;/h2&gt;
&lt;p&gt;Wherever possible, make use of ROS 2 custom messages. Each new system we build starts with a &lt;code&gt;*_msgs&lt;/code&gt; package, which includes message definitions tailored to that stack.&lt;/p&gt;
&lt;p&gt;There are lots of reasons to do this other than performance: it makes the development experience much better by giving meaningful names to values, it catches semantic “mismatches” caused by improperly hooking up a publisher and subscriber, and it helps &lt;em&gt;make invalid states unrepresentable&lt;/em&gt; (can you tell I’m a Rust fan?).&lt;/p&gt;
&lt;p&gt;For the above example, our team now uses a &lt;code&gt;DriveCommand&lt;/code&gt; which encodes the speed of each of our six motors in a single message, each named semantically:&lt;/p&gt;
&lt;pre class=&quot;language-txt&quot; data-language=&quot;txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;float32 left_front
float32 left_middle
float32 left_back
float32 right_front
float32 right_middle
float32 right_back
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;large-messages-and-fragmentation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#large-messages-and-fragmentation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Large Messages and Fragmentation&lt;/h1&gt;
&lt;p&gt;Our team ran into another issue with ROS 2: heavy fragmentation with large message types.&lt;/p&gt;
&lt;p&gt;While filming a &lt;a href=&quot;https://www.youtube.com/watch?v=ocijXlLQ2Es&quot;&gt;demo video&lt;/a&gt; of our system, we wanted to include a visualization of point-cloud data from our stereoscopic camera (a &lt;a href=&quot;https://www.stereolabs.com/products/zed-2&quot;&gt;Zed 2i&lt;/a&gt; by StereoLabs). Last year, we got the shot (video &lt;a href=&quot;https://youtu.be/-ZSak_HEInE?t=238&quot;&gt;here&lt;/a&gt;):&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2560&quot; height=&quot;1440&quot; src=&quot;https://breq.dev/_astro/point-cloud.O9y_zrVe_ZX6Hoj.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;We had no trouble doing this in ROS 1 — as our communication link degraded, the point cloud data just started to slow down. However, in ROS 2, this demo completely broke down.&lt;/p&gt;
&lt;p&gt;The key difference here is that ROS 1 used TCP, while ROS 2 uses UDP. Each point cloud message is big: our sensor is quite high-resolution and each pixel needs a color and position in space. With a TCP connection, congestion control takes care of things, slowing down the link and causing frames to be dropped to keep things working — this slows down the framerate at the output but otherwise works fine.&lt;/p&gt;
&lt;p&gt;In ROS 2 using a DDS middleware, messages are sent over UDP. However, remember that frames on most networks are limited to an MTU of 1500. When you send a large message using a DDS, the DDS layer just creates a single massive UDP packet, and the OS is responsible for fragmenting it appropriately. If the computer on the other end can defragment it properly, you’re in business. In a congested network, the chances of successful reassembly fall to near-zero, meaning you’ll get nothing out of the other end of the connection.&lt;/p&gt;
&lt;p&gt;Here, you’ve got a few options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Try to control the fragmentation process yourself: avoid sending large messages, instead send smaller ones.&lt;/li&gt;
&lt;li&gt;If you’re able to, try to reduce the traffic over the network link to pull it out of a congested state (and hope for the best).&lt;/li&gt;
&lt;li&gt;Transport this data through a different transport mechanism. (We do this for &lt;a href=&quot;https://breq.dev/2023/06/21/cameras&quot;&gt;image streaming&lt;/a&gt;.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;qos-parameters&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#qos-parameters&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;QoS parameters&lt;/h2&gt;
&lt;p&gt;ROS 2 introduced &lt;a href=&quot;https://docs.ros.org/en/humble/Concepts/Intermediate/About-Quality-of-Service-Settings.html&quot;&gt;Quality-of-Service&lt;/a&gt; parameters on subscribers and publishers. A QoS “profile” includes a few parameters (listed here roughly from most to least important):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;History (Queue Depth):&lt;/strong&gt; The number of samples to keep in the queue. Lower numbers improve efficiency, but may lead to old messages being lost if the network drops for a second or so. A sensor readout can usually have a queue depth of 1 since the system only cares about the last read value, but a button press value should have a larger queue depth or it’s possible to miss inputs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reliability:&lt;/strong&gt; “Best Effort” or “Reliable.” Controls whether publishers should retry transmission to subscribers if it fails.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Durability:&lt;/strong&gt; Whether the publisher holds on to samples to give to subscribers who join after the publisher starts (“Transient Local”) or not (“Volatile”).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deadline:&lt;/strong&gt; The expected maximum amount of time between messages on a topic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lifespan:&lt;/strong&gt; The amount of time until a message becomes stale. Messages which are published but not received until after their lifespan expires are dropped.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Liveliness:&lt;/strong&gt; Whether publishers are implicitly considered alive if they publish to a topic (“Automatic”) or if they must manually use the publisher API to assert liveliness (“Manual By Topic”).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lease Duration:&lt;/strong&gt; The amount of time between assertions of liveliness (see above) before a node is considered to have lost liveliness.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While subscribers and publishers use the same profiles, the work in slightly different ways. A subscriber’s profile is the &lt;em&gt;minimum&lt;/em&gt; quality it’s willing to accept, and the publisher’s profile is the &lt;em&gt;maximum&lt;/em&gt; quality it’s willing to provide. This makes a bit more sense when you look at examples:&lt;/p&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Publisher&lt;/th&gt;&lt;th&gt;Subscriber&lt;/th&gt;&lt;th&gt;Communication&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Reliable&lt;/td&gt;&lt;td&gt;Reliable&lt;/td&gt;&lt;td&gt;Reliable&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Reliable&lt;/td&gt;&lt;td&gt;Best Effort&lt;/td&gt;&lt;td&gt;Best Effort&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Best Effort&lt;/td&gt;&lt;td&gt;Best Effort&lt;/td&gt;&lt;td&gt;Best Effort&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Best Effort&lt;/td&gt;&lt;td&gt;Reliable&lt;/td&gt;&lt;td&gt;&lt;em&gt;(doesn’t work)&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;zenoh&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#zenoh&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Zenoh&lt;/h1&gt;
&lt;p&gt;What if, however, you weren’t at the whimsy of the DDS protocol at all? If you let go of the purism of the DDS standard and instead substituted a more efficient protocol which could better handle the congested, dynamic conditions of a lossy wireless network?&lt;/p&gt;
&lt;p&gt;This is where &lt;a href=&quot;https://zenoh.io/&quot;&gt;Zenoh&lt;/a&gt; comes in. Zenoh is a pub/sub protocol designed with one goal: reduce network overhead as much as possible. Most importantly, it has plugins tailored at interoperating with existing DDS systems.&lt;/p&gt;
&lt;p&gt;When working with ROS 2, Zenoh dramatically reduces the amount of discovery traffic generated (by &lt;a href=&quot;https://zenoh.io/blog/2021-03-23-discovery/&quot;&gt;up to 97%&lt;/a&gt;) compared to a traditional DDS middleware. We’ve also found it to be much better at communication in general even after the discovery state is finished. We’re not the only ones: an Indy Autonomous Challenge team &lt;a href=&quot;https://zenoh.io/blog/2021-09-28-iac-experiences-from-the-trenches/&quot;&gt;made the same observations&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So how is Zenoh some kind of miracle for communication? It’s a well-designed protocol, but more importantly, it’s not held down by the restrictions of the DDS standard. DDS is based on a protocol &lt;a href=&quot;https://web.archive.org/web/20110915193450/http://www.omg.org/news/meetings/GOV-WS/pr/rte-pres/ddsi-demo.pdf&quot;&gt;designed in 2003&lt;/a&gt;, and is designed foremost for interoperability, not efficiency. You can see this in the protocol’s design: instead of defining a standard byte order, it’s defined on each message based on a flags register; information about the DDS system vendor is sent with every packet; each node is required to maintain a full graph of the network; and entity IDs are duplicated between submessages in a packet instead of being sent once per packet.&lt;/p&gt;
&lt;p&gt;ROS 2 was initially pitched as a DDS-centric version of ROS, replacing the bespoke TCPROS with DDS as an open, standard communication protocol. However, frustrations with the complexity of the DDS implementations and their poor performance on lossy wireless networks motivated OpenRobotics to search for an alternate middleware. Unsurprisingly, &lt;a href=&quot;https://discourse.ros.org/uploads/short-url/o9ihvSjCwB8LkzRklpKdeesRTDi.pdf&quot;&gt;they chose Zenoh&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you’re reading this after May 23, 2024 (&lt;a href=&quot;https://en.wikipedia.org/wiki/World_Turtle_Day&quot;&gt;World Turtle Day&lt;/a&gt;), ROS 2 &lt;a href=&quot;https://docs.ros.org/en/jazzy/Releases/Release-Jazzy-Jalisco.html&quot;&gt;Jazzy Jalisco&lt;/a&gt; will be out, and hopefully &lt;a href=&quot;https://discourse.ros.org/t/ros-2-alternative-middleware-report/33771&quot;&gt;&lt;code&gt;rmw_zenoh&lt;/code&gt;&lt;/a&gt; with it.&lt;/p&gt;
&lt;p&gt;In the meantime, we’re stuck running a DDS on each host and using plugins to bridge the gaps. The best approach &lt;em&gt;as of writing&lt;/em&gt; is to use &lt;a href=&quot;https://github.com/eclipse-zenoh/zenoh-plugin-ros2dds&quot;&gt;zenoh-plugin-ros2dds&lt;/a&gt;, setting &lt;code&gt;ROS_DOMAIN_ID&lt;/code&gt; to a different value on each host to prevent DDS communication over the network. Old tutorials used &lt;a href=&quot;https://github.com/eclipse-zenoh/zenoh-plugin-dds&quot;&gt;zenoh-plugin-dds&lt;/a&gt;, which works for ROS 2 applications but doesn’t support ROS 2 tooling as well.&lt;/p&gt;
&lt;p&gt;Zenoh ended up being the solution to our team’s DDS dilemma. We plan to run Zenoh at the 2024 University Rover Challenge.&lt;/p&gt;
&lt;h2 id=&quot;udp-vs-tcp&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#udp-vs-tcp&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;UDP vs TCP&lt;/h2&gt;
&lt;p&gt;When running over a network, Zenoh can run over either TCP or UDP. Typically, TCP is usually chosen as a network protocol for its reliable transmission guarantees, so it may seem like a poor fit for a system where some topics only need best-effort transmission. We’ve found, however, that our system tends to work much better over TCP than UDP, and we believe this is due to the TCP congestion control mechanism.&lt;/p&gt;
&lt;p&gt;TCP uses a complex &lt;a href=&quot;https://en.wikipedia.org/wiki/TCP_congestion_control&quot;&gt;congestion control&lt;/a&gt; algorithm to avoid sending too much traffic over a congested network and causing undesirable behavior. With our radio network, the constrained link is specifically between our two IP radios, Each radio has a full 100 Mbps link to its computer (either the Jetson on the rover or the laptop in the base station), so the computers running ROS have no direct knowledge of network conditions. Then, when naive UDP protocols like DDS send way too much traffic over the network, it causes packets to be dropped on the radios, and the only feedback each host receives is a lack of acknowledgement after a timeout. On the other hand, TCP congestion control minimizes packet drops in the system, causing more reliable transmission.&lt;/p&gt;
&lt;h1 id=&quot;ros2-sunburst&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#ros2-sunburst&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;ros2-sunburst&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;In true breq.dev tradition, this post leaves off with a tool I’ve written to help solve some tiny facet of this problem. In this case, it’s a bandwidth usage visualization tool for ROS 2 traffic.&lt;/p&gt;
&lt;p&gt;Here’s a plot of our team’s bandwidth usage by topic:&lt;/p&gt;
&lt;iframe src=&quot;/plots/ros-bandwidth.html&quot; class=&quot;w-full&quot; height=&quot;750&quot;&gt;
In ROS, topics tend to follow a hierarchy structure: your robot’s drive stack might fall under /drive&lt;/iframe&gt;, commands might be under &lt;code&gt;/drive/cmd_vel&lt;/code&gt;, etc. This structure isn’t inherent to the transport protocol in any sense, but it does help you reason about the parts of your system which are using the most bandwidth.&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Using this tool works as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Capture a sample of DDS traffic over your network with Wireshark. (Ensure you include the initial discovery traffic — otherwise there is no way to map DDS writer/reader IDs to ROS 2 topic names).&lt;/li&gt;
&lt;li&gt;Run this script on your packet capture file.&lt;/li&gt;
&lt;li&gt;View the output in the terminal (the top topic names by value) and in the plot (the starburst visualization).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can grab the script from this &lt;a href=&quot;https://gist.github.com/breqdev/6968528b947ab1b3ddcaf0b69bc64e12&quot;&gt;gist&lt;/a&gt;.&lt;/p&gt;
&lt;gist id=&quot;6968528b947ab1b3ddcaf0b69bc64e12&quot;&gt;&lt;/gist&gt;</content:encoded></item><item><title>Radios for Robots</title><link>https://breq.dev/2024/04/16/radios/</link><guid isPermaLink="true">https://breq.dev/2024/04/16/radios/</guid><description>For when you want your &apos;bots to roam outside the range of your apartment WiFi.</description><pubDate>Tue, 16 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Radios are often an afterthought when designing a robotics project. Few robots are self-contained: almost all need to communicate with some external system, be that a basic remote control or a complex offboard system for advanced processing.&lt;/p&gt;
&lt;p&gt;Most useful robots also need to operate in an uncontrolled environment. A Roomba needs to connect to a customer’s Wi-Fi network, a self-driving car needs to operate reliably regardless of local RF conditions, and a photography drone can’t fall out of the sky if an FPV racer sets up shop at the next field over.&lt;/p&gt;
&lt;p&gt;My experience with this is primarily in the space of wheeled, “prototype-scale” robots of varying sizes. There is much to consider when scaling up a system beyond one or two prototypes, and the math tends to work out differently for flying ‘bots compared to terrestrial ones. (And if what you’re building is a boat, good luck — RF and water do not mix well, and the &lt;a href=&quot;https://en.wikipedia.org/wiki/Fresnel_zone&quot;&gt;Fresnel Zone&lt;/a&gt; is your enemy.)&lt;/p&gt;
&lt;h1 id=&quot;hardware&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#hardware&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Hardware&lt;/h1&gt;
&lt;p&gt;Radio hardware is built for a wide range of applications, meaning it’s difficult to even enumerate the options available to you. Especially for a prototype or hobby project, expect to look in interesting places for what you need to make things work.&lt;/p&gt;
&lt;h2 id=&quot;consumer-grade-networking&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#consumer-grade-networking&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Consumer-Grade Networking&lt;/h2&gt;
&lt;p&gt;We’ve gotten quite good at building cheap radios to connect consumer devices over short distances, over Wi-Fi, Bluetooth, or other protocols. While these chips might have been designed for things like smart lightbulbs or wireless headphones, they’re often a great choice for short-range networks and can often provide substantial throughput.&lt;/p&gt;
&lt;h3 id=&quot;your-built-in-wi-fi&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#your-built-in-wi-fi&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Your built-in Wi-Fi&lt;/h3&gt;
&lt;p&gt;Sometimes, the best radio is the one you already have. If your project uses a recent Raspberry Pi model, and your laptop has a Wi-Fi chip, then you’re all set for short range communications!&lt;/p&gt;
&lt;p&gt;With Wi-Fi, one device must be the &lt;em&gt;access point&lt;/em&gt; and the rest are &lt;em&gt;stations&lt;/em&gt; (clients). There are a few advantages of making your robot the access point:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You don’t need to configure the robot with the details of your existing Wi-Fi network&lt;/li&gt;
&lt;li&gt;You can operate the robot in areas without an existing network&lt;/li&gt;
&lt;li&gt;You have full control over the channel on which the network operates&lt;/li&gt;
&lt;li&gt;It’s easy to connect more devices (e.g., encouraging nontechnical users to connect their phones to try operating your creation!)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a great option for running your robot at events where you won’t know the setup beforehand, provided you don’t expect too many other robots taking up their slice of the 2.4 GHz spectrum.&lt;/p&gt;
&lt;p&gt;But it does come with a major limitation: It is generally quite difficult to create a setup in which your robot, as the access point, is able to access the internet via one of its clients. If your laptop is a station on your robot’s network, then whoops, it can’t be on your home network anymore. Even if you plug your laptop into an Ethernet connection with internet access, good luck getting your laptop to route traffic from your robot to the Internet and back. It can be done, but it takes some complicated network configuration that isn’t fun to deal with.&lt;/p&gt;
&lt;h3 id=&quot;bluetooth&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#bluetooth&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Bluetooth&lt;/h3&gt;
&lt;p&gt;Bluetooth is great as a “it just works” system, requiring just the initial pairing flow before you’re off to the races. I used it to send commands to the &lt;a href=&quot;https://breq.dev/projects/outshine&quot;&gt;LED choker&lt;/a&gt; that I made. You can get up and running quickly with a microprocessor with Bluetooth support (like the &lt;a href=&quot;https://www.adafruit.com/product/4062&quot;&gt;nRF52480&lt;/a&gt;) or a drop-in Bluetooth Serial module (like the &lt;a href=&quot;https://www.amazon.com/HiLetgo-Wireless-Bluetooth-Transceiver-Arduino/dp/B071YJG8DR&quot;&gt;HC-05&lt;/a&gt; available from tons of sketchy Amazon sellers).&lt;/p&gt;
&lt;p&gt;Bluetooth modules are typically much lower power than their Wi-Fi counterparts, don’t usually work in a scenario more complex than a point-to-point connection, and have substantially worse throughput. Different Bluetooth profiles support sending different amounts of throughput through, but something like the &lt;a href=&quot;https://www.bluetooth.com/specifications/specs/serial-port-profile-1-1/&quot;&gt;Serial Port Profile&lt;/a&gt; will struggle to transmit images or video.&lt;/p&gt;
&lt;p&gt;Bluetooth also doesn’t typically use the TCP/IP stack (although it can be done with a specific profile). This is typically fine for microcontroller-based robots, but can make development annoying if your robot runs Linux.&lt;/p&gt;
&lt;h3 id=&quot;external-wi-fi-routers-and-cards&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#external-wi-fi-routers-and-cards&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;External Wi-Fi routers and cards&lt;/h3&gt;
&lt;p&gt;Still within the consumer realm, some off-the-shelf Wi-Fi routers and cards can be great for getting a bit more performance or range out of a Wi-Fi based system.&lt;/p&gt;
&lt;p&gt;Consider a Wi-Fi router on your robot as an extension to the “robot as access point” idea in the prior sections. You get a physical piece of hardware with a sole purpose of providing a Wi-Fi network, along with Ethernet ports to connect any computers onboard your robot. Wi-Fi routers typically use higher power, have better antennas, and can provide more throughput than (ab)using your device’s built-in radio.&lt;/p&gt;
&lt;p&gt;Alternatively, you can put the router at a base station and connect it (e.g. via Ethernet) to give it Internet access. This gives you the benefits of a wireless network entirely under your control while still giving your robot and ground station Internet access for development.&lt;/p&gt;
&lt;p&gt;On the station end, you can still improve compared to whatever built-in adapter you have. Some UAV enthusiasts working on the &lt;a href=&quot;https://github.com/OpenHD/OpenHD&quot;&gt;OpenHD project&lt;/a&gt; have identified the &lt;a href=&quot;https://openhd.gitbook.io/open-hd/hardware/wifi-adapters&quot;&gt;Realtek RTL8812AU and friends&lt;/a&gt; to be a strong contender, having the maximum allowed 500mW power output, two antennas for diversity and MIMO operation, and support for both the 2.4 GHz and 5.8 GHz bands (which we’ll discuss more in the &lt;em&gt;Band Planning&lt;/em&gt; section).&lt;/p&gt;
&lt;h3 id=&quot;esp32-chips&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#esp32-chips&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;ESP32 chips&lt;/h3&gt;
&lt;p&gt;Back in my day, all we had was the ESP8266, a cheap board with no GPIO, bad hardware support, and acceptable 2.4 GHz Wi-Fi in a very cheap package. Nowadays, Espressif Systems has unveiled a line of ESP32 boards with ample GPIO, fast clock speeds, and both Wi-Fi and Bluetooth capabilities.&lt;/p&gt;
&lt;p&gt;Choosing an ESP32-based design gives you lots of options for small robots, either as the robot’s primary microcontroller or as a peripheral. For instance, you can use Bluetooth to set up the initial connection, then let the user input their Wi-Fi details and switch to that connection. Or, you can use Bluetooth for telemetry and controls, while keeping a Wi-Fi link during development for stronger visibility.&lt;/p&gt;
&lt;p&gt;Of course, this series of chips is in the microcontroller realm, so they won’t be able to push through as much throughput as a dedicated Wi-Fi router or card. for small and short-range projects, however, they can be a strong contender.&lt;/p&gt;
&lt;h2 id=&quot;drones&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#drones&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Drones&lt;/h2&gt;
&lt;p&gt;The world of UAVs has brought forth many unique solutions for radio communication. Unlike wheeled robots, you can typically assume that a drone will maintain line-of-sight with the control station at all times, giving signal penetration a lower priority. Drones also are extremely sensitive to weight, so solutions tend to be compact and simple.&lt;/p&gt;
&lt;p&gt;One quirk of drone communications is that drones typically use two frequencies: one for control and basic telemetry, and another for FPV/video streaming, leading to two very different sets of solutions.&lt;/p&gt;
&lt;p&gt;For controls, the &lt;a href=&quot;https://rfdesign.com.au/products/rfd900-modem/&quot;&gt;RFD900&lt;/a&gt; is by far the most well-known choice. It provides a basic serial connection which typically facilitates the transfer of &lt;a href=&quot;https://mavlink.io/en/&quot;&gt;Mavlink&lt;/a&gt; messages. Unlike any of the other solutions we’ve looked at thus far, the RFD900 works on the 900 MHz band.&lt;/p&gt;
&lt;p&gt;The RFD900 uses &lt;em&gt;frequency-hopping spread spectrum&lt;/em&gt;: the specific frequency used will jump around in the 900 MHz band to avoid interference. This is great in a scenario with other RFD900s (like an FPV drone racing event) since they’ll generally stay out of each other’s way.&lt;/p&gt;
&lt;p&gt;FPV drones also typically use &lt;em&gt;analog&lt;/em&gt; video systems. There are a few reasons why these have stuck around, even though they use spectrum far less efficiently than digital systems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Since digital systems need to perform video encoding, analog video requires much less hardware, saving weight&lt;/li&gt;
&lt;li&gt;Analog video tends to degrade gracefully under interference or poor signal conditions&lt;/li&gt;
&lt;li&gt;Since drones typically operate under line-of-sight conditions, they can use higher bands (most commonly, 5.8 GHz) which allow for wider channels&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because of this, analog video gear is generally commonly available. If you’re working with a small robot with only a single camera over a relatively short distance, this can be a good solution. However, note that you can’t perform any onboard processing with such a camera.&lt;/p&gt;
&lt;h2 id=&quot;wisp-gear&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#wisp-gear&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;WISP Gear&lt;/h2&gt;
&lt;p&gt;Wireless Internet Service Providers, or WISPs, are companies that provide Internet access to customers using wireless links as opposed to wired (fiber or copper) connections. Equipment made for WISPs is built to be reliable, designed for long-term operation, highly configurable, and to operate right up to the power limits allowed by law.&lt;/p&gt;
&lt;p&gt;Ubquiti products are cheap and work well, including the &lt;a href=&quot;https://store.ui.com/us/en/products/rocketm2&quot;&gt;Rocket&lt;/a&gt; and &lt;a href=&quot;https://store.ui.com/us/en/products/bulletm2-hp&quot;&gt;Bullet&lt;/a&gt; series of radios. Each line comes in 2 GHz and 5 GHz variants, with 900 MHz variants discontinued but often found on eBay.&lt;/p&gt;
&lt;p&gt;For stronger 900 MHz capabilities, consider the Cambium &lt;a href=&quot;https://www.cambiumnetworks.com/products/backhaul/ptp-450-900-mhz/&quot;&gt;PTP 450 900&lt;/a&gt;. Note that these use a proprietary protocol. While Cambium makes some other variants, they’re either difficult to get licenses for (like the PTP 450 on 3.65 GHz) or prohibitively expensive for most use cases (like the PMP line, which supports point-to-multipoint scenarios across many band types).&lt;/p&gt;
&lt;p&gt;These radios provide an Ethernet port and can effectively make your network look like a flat Ethernet subnet, greatly simplifying your configuration. Alternatively, many Ubiquiti radios can be configured to operate as a standards-compliant Wi-Fi hotspot instead of in point-to-point mode, allowing you to combine a high-powered WISP radio with inexpensive consumer Wi-Fi gear.&lt;/p&gt;
&lt;h2 id=&quot;packet-radio&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#packet-radio&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Packet Radio&lt;/h2&gt;
&lt;p&gt;A promising method I’ve been looking for an excuse to use is &lt;a href=&quot;https://learn.adafruit.com/adafruit-rfm69hcw-and-rfm96-rfm95-rfm98-lora-packet-padio-breakouts&quot;&gt;low-cost packet radio modules&lt;/a&gt;. These implement a barebones protocol without any sort of pairing, connection, or scanning logic. They typically support relatively slow data rates, but can cover a relatively large area.&lt;/p&gt;
&lt;p&gt;These radios tend to transmit in one of two bands:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;433 MHz: Generally unlicensed in Europe, licensed in the Americas (although usable with an Amateur Radio license).&lt;/li&gt;
&lt;li&gt;900 MHz: Unlicensed in both Europe and the Americas (albeit slightly different bands, 868 vs 915 depending on region). This can be a blessing (it’s easier to get started with) or a curse (in the US/Canada, there are many more devices in this band than in 433).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An iteration of these uses &lt;a href=&quot;https://lora-alliance.org/&quot;&gt;LoRa&lt;/a&gt;, a modulation technique that enables much farther communication (although this increases cost).&lt;/p&gt;
&lt;h2 id=&quot;amateur-hardware&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#amateur-hardware&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Amateur Hardware&lt;/h2&gt;
&lt;p&gt;Another source of radios is hardware designed for amateur radio use. These operate on a huge range of bands, but typically focus on voice transmissions. The digital modes that do exist are intended for short, infrequent messages. This is typically not a good fit for a robotics scenario, but it could be useful.&lt;/p&gt;
&lt;p&gt;On the absolute low end, you can always use a cheap &lt;a href=&quot;https://www.baofengradio.com/products/uv-5r&quot;&gt;Baofeng radio&lt;/a&gt; (usually ~$20 USD on Amazon) with your computer’s &lt;a href=&quot;https://digirig.net/product/baofeng-direct-to-computer-audio-cable/&quot;&gt;sound card&lt;/a&gt; and some software for a basic digital mode.&lt;/p&gt;
&lt;p&gt;Of course, amateur hardware also has restrictions (e.g., no commercial use) which we’ll talk about more when we discuss amateur licensing.&lt;/p&gt;
&lt;h1 id=&quot;band-planning&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#band-planning&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Band Planning&lt;/h1&gt;
&lt;p&gt;When picking a radio, you’ll often want to consider the frequency band it transmits on. Specific bands have certain innate characteristics which make them more or less suited to a given application. You’ll also need to consider other radio systems within your robot and ensure you do not cause interference.&lt;/p&gt;
&lt;p&gt;Different bands have different strengths and weaknesses. Generally, lower frequency bands will provide better signal penetration and non-line-of-sight operation (e.g., operating your robot from the other side of a wall), while higher frequencies are better suited to line-of-sight or short-range uses.&lt;/p&gt;
&lt;p&gt;An advantage of higher-frequency bands is that they generally can provide much higher bandwidth, both via higher signal rates and wider channels. If your system involves streaming lots of high-resolution video, point clouds, etc., you will probably struggle on a lower-frequency setup.&lt;/p&gt;
&lt;p&gt;If your robot has multiple communication systems, you’ll need to make sure you aren’t causing interference to yourself. This could take the form of multiple links to your base station (such as separate links for telemetry and video), or it could arise in a system with multiple interacting robots.&lt;/p&gt;
&lt;p&gt;Generally, for multiple radio links to your base station, you will want to put each link on a separate band to minimize interference. For instance, many drone pilots use 900 MHz or 2.4 GHz for teleoperation and 5.8 GHz for video streaming.&lt;/p&gt;
&lt;p&gt;Interference can also be tricky to identify the source of. On the Rover team, we noticed an issue with our 900 MHz radio gear interfering with our high-precision GPS receivers, preventing us from attaining a precise fix.&lt;/p&gt;
&lt;p&gt;Your robot might also need to operate in an area with lots of interference, such as a Maker Faire, in which the sheer number of users on the 2.4 GHz and 5 GHz bands can make protocols like Wi-Fi almost unusable. While &lt;a href=&quot;https://jpieper.com/2020/04/01/spread-spectrum-rf-control-and-telemetry/&quot;&gt;frequency-hopping protocols&lt;/a&gt; can work more reliably, they still have their limitations.&lt;/p&gt;
&lt;h1 id=&quot;amateur-licensing&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#amateur-licensing&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Amateur Licensing&lt;/h1&gt;
&lt;p&gt;If you’re a hobbyist, you aren’t just limited to the ISM or unlicensed bands: the Amateur Radio Service is there for you! Getting an amateur license gives you access to a much larger array of bands across a wide range of radio spectrum. While this varies by country, most countries do something similar to the U.S.:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Band chart image created by the ARRL&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1009&quot; height=&quot;768&quot; src=&quot;https://breq.dev/_astro/amateur-band-chart.CCriB6kX_q2q8r.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The most important restrictions to keep in mind are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your use-case must be for a non-commercial purpose.&lt;/li&gt;
&lt;li&gt;You generally cannot use encryption (although there’s some &lt;a href=&quot;https://www.qsl.net/kb9mwr/projects/wireless/Data%20Encryption%20is%20Legal.pdf&quot;&gt;debate&lt;/a&gt; on this). You definitely can’t use encryption to hide the content of your messages.&lt;/li&gt;
&lt;li&gt;Any data protocol you use must be publicly documented.&lt;/li&gt;
&lt;li&gt;You need to obtain an amateur license, and you must identify with your callsign once every 10 minutes during transmission.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Getting licensed is generally straightforward (&lt;a href=&quot;https://beckystern.com/2020/03/08/my-experience-getting-licensed-in-ham-radio/&quot;&gt;Becky Stern’s article&lt;/a&gt; gives a good overview of the process). A few tips from my own journey:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If all you care about is passing the test, just go on &lt;a href=&quot;https://hamstudy.org/&quot;&gt;hamstudy.org&lt;/a&gt; and memorize the questions for a few hours. There are resources that you can pay for to learn the concepts in more detail, but it’s not necessary.&lt;/li&gt;
&lt;li&gt;You’ll initially be assigned a random callsign, but you can &lt;a href=&quot;http://www.arrl.org/applying-for-a-vanity-call&quot;&gt;apply for a vanity call&lt;/a&gt; immediately afterwards for a small fee.&lt;/li&gt;
&lt;li&gt;If you’re a college student, see if there’s a club on campus that runs exams!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the U.S., there are a few classes of license, and most countries follow a similar structure. At time of writing, I hold a technician class license, and it gives me almost all of the same privileges as the higher classes on the shorter bands (433 MHz and up).&lt;/p&gt;
&lt;p&gt;Some amateur bands overlap with unlicensed/ISM bands: the 33 centimeter amateur band takes the same space as the 915 MHz ISM band, the 2.4 GHz amateur band partially overlaps the ISM one, and the 5.8 GHz amateur band is a subset of the spectrum set aside for Wi-Fi. In these bands, you can often &lt;em&gt;use commercial grade hardware at higher power limits or with larger antennas&lt;/em&gt; — for instance, running a Ubiquiti access point on 2412 MHz at maximum power with a high-gain antenna. This gear generally isn’t designed for use above the unlicensed limits, so you won’t always gain a significant boost, but it can still give you a bit more range.&lt;/p&gt;
&lt;p&gt;Other bands are generally pretty quiet except for amateur users:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The 2 meter and 70 centimeter bands are predominantly used for analog FM voice, although the 70 cm band overlaps with the unlicensed 433 MHz band in Europe (meaning you can find consumer hardware which operates on it)&lt;/li&gt;
&lt;li&gt;The 23 centimeter (1240 MHz) band is generally pretty empty, although it is right next to the frequencies used by GNSS (e.g., GPS) receivers which can cause problems, and it’s tough to find hardware for&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;conclusion&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Based on my experience so far, I’ve developed a few rules for selecting which system to use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Start with Wi-Fi.&lt;/strong&gt; It covers a surprising number of use cases regarding range and data throughput, and hardware is common, cheap, and lightweight.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Punch through obstacles using either higher power or larger wavelength.&lt;/strong&gt; If you’re going a significant distance through a forest, for instance, you’ll probably struggle with consumer Wi-Fi modules. You could switch to a larger wavelength like 900 MHz, which can &lt;a href=&quot;https://www.reddit.com/r/fpv/comments/193tgt/fpv_live_comparison_of_58ghz_vs_900mhz/&quot;&gt;better penetrate obstacles&lt;/a&gt;, or you can stay on 2.4 GHz but switch to following amateur regulations and using WISP gear and high-gain antennas.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Consider more specialized systems if needed.&lt;/strong&gt; You might struggle to find components to run on other bands or using different protocols, but it’s sometimes the only way to satisfy an unusual use case.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>88x31 Buttons and Network Science</title><link>https://breq.dev/2023/12/26/88x31-science/</link><guid isPermaLink="true">https://breq.dev/2023/12/26/88x31-science/</guid><description>Applying principles of network science to those tiny little buttons.</description><pubDate>Tue, 26 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;background&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#background&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Background&lt;/h1&gt;
&lt;p&gt;You might’ve noticed the tiny buttons/badges at the footer of this site and others, each showing a pixel-art design and linking to another website. These are called &lt;em&gt;88x31 buttons&lt;/em&gt;, and they’re a de-facto standard of the indie web. Sources on this are few and far between, but &lt;a href=&quot;https://neonaut.neocities.org/cyber/88x31&quot;&gt;The Neonauticon&lt;/a&gt; identified the start to be Netscape, which published “Netscape Now” buttons that sites could add to show their use for then-new web features.&lt;/p&gt;
&lt;p&gt;People took this idea and ran with it, placing 88x31 buttons for themselves, their friends, and projects they supported in their websites, forum signatures, etc. Similar to &lt;a href=&quot;https://en.wikipedia.org/wiki/Webring&quot;&gt;webrings&lt;/a&gt;, 88x31 buttons provided a fun way for people to find related content to a given page.&lt;/p&gt;
&lt;p&gt;My friends and I are working on &lt;a href=&quot;https://eightyeightthirty.one/&quot;&gt;mapping the entire 88x31 graph&lt;/a&gt;. The result of our work is the first ever snapshot of the entire 88x31 network, available in &lt;a href=&quot;https://eightyeightthirty.one/graph.json&quot;&gt;a single JSON file&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Relatedly, a few semesters ago, I took a course on &lt;a href=&quot;https://en.wikipedia.org/wiki/Network_science&quot;&gt;network science&lt;/a&gt;. One of the main takeaways was that lots of real-world networks exhibit something called the &lt;a href=&quot;https://en.wikipedia.org/wiki/Small-world_network&quot;&gt;&lt;em&gt;small-world property&lt;/em&gt;&lt;/a&gt;, specifically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;High &lt;em&gt;clustering&lt;/em&gt; (you are more likely to be friends with your friends’ friends)&lt;/li&gt;
&lt;li&gt;Low &lt;em&gt;distance&lt;/em&gt; (the typical distance between two randomly-chosen nodes stays small as the graph grows)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We see this phenomenon across all sorts of network types: the original 1998 &lt;a href=&quot;https://edisciplinas.usp.br/pluginfile.php/4205021/mod_resource/content/1/NAture_Watts_Strogatz.pdf&quot;&gt;Watts &amp;#x26; Strogatz paper&lt;/a&gt; uses networks of film actors, the electrical grid, and the neural network of a worm.&lt;/p&gt;
&lt;p&gt;So: we’ve got a brand new dataset and some criteria to test. Does the 88x31 graph exhibit the small-worlds property?&lt;/p&gt;
&lt;h1 id=&quot;analysis&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#analysis&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Analysis&lt;/h1&gt;
&lt;p&gt;To analyze the behavior of the graph as it expands, I’ll be using two datasets: one with 4428 nodes that we obtained partway through the scraping work, and the most recent dataset with 16023 nodes. Analysis was done using &lt;a href=&quot;https://gephi.org/&quot;&gt;Gephi&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;clustering&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#clustering&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Clustering&lt;/h2&gt;
&lt;p&gt;Clustering is the measure of how locally-connected a graph is. Think about a social graph: if everyone had a set of, say, 20 friends randomly chosen from a group of 1 million, you’d have quite low clustering. However, typically, you’re much more likely to be friends with your friends’ friends, implying high clustering. Clustering is quantified by the &lt;em&gt;clustering coefficient&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The formal definition of a graph is a set of vertices (nodes) &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;V&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;V&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.22222em;&quot;&gt;V&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; and a set of edges (links) &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;E&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, such that:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;G&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;V&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;G = (V, E)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;G&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.22222em;&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;We write that edge &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/mrow&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;e_{ij}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7167em;vertical-align:-0.2861em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.05724em;&quot;&gt;ij&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; connects vertex &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;v_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.5806em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; with vertex &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;v_j&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7167em;vertical-align:-0.2861em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.05724em;&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;To define clustering, it’s useful to define the &lt;em&gt;neighborhood&lt;/em&gt; of a vertex &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;N&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;N_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.109em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; as the set of all of its connected neighbors, considering both links “in” and links “out”:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;N&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;{&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;:&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/mrow&gt;&lt;/msub&gt;&lt;mo&gt;∈&lt;/mo&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;mo&gt;∨&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/mrow&gt;&lt;/msub&gt;&lt;mo&gt;∈&lt;/mo&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;}&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;N_i = \{v_j : e_{ij} \in E \vee e_{ji} \in E \}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.109em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.0361em;vertical-align:-0.2861em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.05724em;&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8252em;vertical-align:-0.2861em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.05724em;&quot;&gt;ij&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;∈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;∨&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8252em;vertical-align:-0.2861em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;ji&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;∈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Let’s go a step further and define the &lt;em&gt;degree&lt;/em&gt; of a vertex &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;k_i&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8444em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03148em;&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0315em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; as the number of connected neighbors it has, i.e.,&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;∣&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;N&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;∣&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;k_i = |N_i|&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8444em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03148em;&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0315em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;∣&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.109em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;∣&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;where we use the vertical bars to mean the cardinality (number of items contained within) the set of neighbors.&lt;/p&gt;
&lt;p&gt;How many links &lt;em&gt;could&lt;/em&gt; exist within the neighborhood of a node? Let’s ignore self-loops, so the number of possible links is &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;k_i \cdot (k_i - 1)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8444em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03148em;&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0315em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03148em;&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0315em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; — each node can connect to each of the other nodes.&lt;/p&gt;
&lt;p&gt;The number of links &lt;em&gt;actually&lt;/em&gt; in the neighborhood can be written as:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;{&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;/mrow&gt;&lt;/msub&gt;&lt;mo&gt;:&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;∈&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;N&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;/mrow&gt;&lt;/msub&gt;&lt;mo&gt;∈&lt;/mo&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;}&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\{ e_{jk} : v_j, v_k \in N_i, e_{jk} \in E \}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.0361em;vertical-align:-0.2861em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3361em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.03148em;&quot;&gt;jk&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8252em;vertical-align:-0.2861em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.05724em;&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3361em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.03148em;&quot;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;∈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9694em;vertical-align:-0.2861em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.109em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3361em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.03148em;&quot;&gt;jk&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;∈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;And thus, the clustering coefficient for a given node is:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;C&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;∣&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;{&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;/mrow&gt;&lt;/msub&gt;&lt;mo&gt;:&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;/msub&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;∈&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;N&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;j&lt;/mi&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;/mrow&gt;&lt;/msub&gt;&lt;mo&gt;∈&lt;/mo&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;}&lt;/mo&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;∣&lt;/mi&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;k&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;C_i = \frac{|\{ e_{jk} : v_j, v_k \in N_i, e_{jk} \in E \}|}{k_i(k_i-1)}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07153em;&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0715em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.363em;vertical-align:-0.936em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.427em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03148em;&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0315em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03148em;&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0315em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;∣&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3361em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.03148em;&quot;&gt;jk&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.05724em;&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3361em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.03148em;&quot;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;∈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3117em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.109em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3361em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.03148em;&quot;&gt;jk&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;∈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;∣&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.936em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;We can use software to compute the clustering for each node, and take the average across all of them. The average clustering coefficient is:&lt;/p&gt;

















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Nodes&lt;/th&gt;&lt;th&gt;Clustering Coefficient&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;4428&lt;/td&gt;&lt;td&gt;0.122&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;16023&lt;/td&gt;&lt;td&gt;0.123&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Is this value high enough to indicate high clustering? For comparison, let’s construct two random graphs with the same number of nodes and edges as each of our sample datasets: one with 4428 nodes and 17254 edges, and one with 16023 nodes and 57202 edges. Gephi supports the &lt;a href=&quot;https://en.wikipedia.org/wiki/Erd%C5%91s%E2%80%93R%C3%A9nyi_model&quot;&gt;Erdős–Rényi model&lt;/a&gt;, which generates a graph based on the number of nodes and the probability of an edge between any two given nodes. Based on the real graph, we can determine the probability numbers as:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;17254&lt;/mn&gt;&lt;mrow&gt;&lt;mn&gt;4428&lt;/mn&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mn&gt;4427&lt;/mn&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0.0008802&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{17254}{4428 \cdot 4427} = 0.0008802&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;4428&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;4427&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;17254&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0.0008802&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;57202&lt;/mn&gt;&lt;mrow&gt;&lt;mn&gt;1602&lt;/mn&gt;&lt;msup&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0.0002228&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{57202}{16023^2} = 0.0002228&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1602&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;57202&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0.0002228&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;I needed to multiply these by 2 to get them to load into Gephi properly; perhaps there is some correction going on for undirected vs directed graphs.&lt;/p&gt;
&lt;p&gt;Finally, we can get the average clustering coefficient for each of these graphs:&lt;/p&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Nodes&lt;/th&gt;&lt;th&gt;Clustering (88x31)&lt;/th&gt;&lt;th&gt;Clustering (Random)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;4428&lt;/td&gt;&lt;td&gt;0.122&lt;/td&gt;&lt;td&gt;0.001&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;16023&lt;/td&gt;&lt;td&gt;0.123&lt;/td&gt;&lt;td&gt;0.000&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Note that the precision of these values is limited by the output of Gephi to 3 decimal places — those values really are that low!&lt;/p&gt;
&lt;p&gt;Our random graph has low (sometimes called &lt;em&gt;vanishing&lt;/em&gt;) clustering as the number of nodes increases. This is the typical behavior for a random graph to have — as graphs grow, the likelihood of a given node being connected to another node decreases, regardless if those nodes are linked by an intermediate node.&lt;/p&gt;
&lt;p&gt;In contrast, our 88x31 graph has high (or &lt;em&gt;nonvanishing&lt;/em&gt;) clustering even as the number of nodes increases. This is a notable property in and of itself, and brings us halfway to demonstrating the small-worlds property!&lt;/p&gt;
&lt;h2 id=&quot;distance&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#distance&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Distance&lt;/h2&gt;
&lt;p&gt;The &lt;em&gt;average path length&lt;/em&gt; &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;L&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;L&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; of a graph is the average of the length of the shortest path between each pair of nodes, ignoring node pairs with no connecting path.&lt;/p&gt;
&lt;p&gt;We can compute the average path length of our graph to be:&lt;/p&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Nodes (&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;N&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;N&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;N&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;)&lt;/th&gt;&lt;th&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;N&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\log(N)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;N&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/th&gt;&lt;th&gt;Avg. Path Length &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;L&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;L&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;4428&lt;/td&gt;&lt;td&gt;8.396&lt;/td&gt;&lt;td&gt;7.182&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;16023&lt;/td&gt;&lt;td&gt;9.681&lt;/td&gt;&lt;td&gt;11.269&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;For the network to exhibit the small-worlds property, we need the average path length to be proportional to the logarithm of the number of nodes — that’s why I’ve added it to the table above.&lt;/p&gt;
&lt;p&gt;Between graphs, the average path length increased by a factor of &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;1.57&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;1.57&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1.57&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, while the number of nodes increased by a factor of &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;3.62&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;3.62&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;3.62&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; (and its logarithm increased by &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;1.15&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;1.15&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1.15&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;). This isn’t exactly on the expected value for &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo&gt;∝&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mi&gt;N&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;L \propto \log N&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;∝&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8889em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;N&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; (where &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo&gt;∝&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\propto&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;∝&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; means “proportional to”), but it’s also quite clearly less than the amount that &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;N&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;N&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;N&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; increased, meaning &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo&gt;≪&lt;/mo&gt;&lt;mi&gt;N&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;L \ll N&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7224em;vertical-align:-0.0391em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≪&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;N&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. Personally, I’m comfortable calling this bound satisfied.&lt;/p&gt;
&lt;p&gt;A few reasons come to mind as to why we might not be getting the expected value here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;N&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;N&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;N&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; is small enough that this is just noise in the dataset. 16K nodes isn’t &lt;em&gt;that&lt;/em&gt; big.&lt;/li&gt;
&lt;li&gt;The topology of the two 88x31 graphs is different. The first graph consists of the nodes discovered off of &lt;a href=&quot;https://notnite.com/&quot;&gt;notnite.com&lt;/a&gt; in a few hours, while the second graph is the entire network as identified by our scrapers. The first graph is going to skew towards the English-speaking, indie web, and LGBT communities, while the second graph includes more Italian and Brazilian communities, tabletop RPG forums, and other groups which are broadly separate from the cluster in which the scraper started. Qualitatively, these other groups seem less interconnected compared to the initial dataset.&lt;/li&gt;
&lt;li&gt;The second graph has a slightly different scraper implementation which analyses the HTML statically instead of using a webdriver to load the page, potentially missing more links on sites which use client-side rendering techniques.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;hubs&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#hubs&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Hubs&lt;/h2&gt;
&lt;p&gt;Real-world graphs often have &lt;em&gt;hubs&lt;/em&gt;. In a transportation network, hubs are points with connections to lots of other places; in the Internet, hubs are major ASNs like Hurricane Electric; in a social graph, hubs are just people who know a lot of other people.&lt;/p&gt;
&lt;p&gt;We can see if our 88x31 graph exhibits this same behavior by looking at the &lt;em&gt;degree distribution&lt;/em&gt; of the graph. For this, I generated the degree of each node in the full 88x31 graph in Gephi, then exported the table into Python to take a histogram and then into Google Sheets to make charts.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;600&quot; height=&quot;371&quot; src=&quot;https://breq.dev/_astro/distribution.ClMpvyHB_1e8zEN.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Above is a log-log plot of the degree distribution of the full 88x31 graph. Let’s walk through it, since it’s not the easiest thing to digest.&lt;/p&gt;
&lt;p&gt;Each x-value represents a degree which a node could have, and the corresponding y-value is the number of nodes with that degree. Per the chart, there are about 10,000 nodes with degree 1, but closer to 500 with degree 10, and only a handful with degree 100.&lt;/p&gt;
&lt;p&gt;The “clumping” taking place at the bottom is since we’re working with a discrete number of nodes. For each of the higher degrees, we’ll probably only see only 1 or 2 nodes with that degree, and whether it’s 1 node or 2 nodes is up to random chance. Since there’s nothing in between, we see those two lines at the bottom of the graph, and we can notice quite a bit more noise there as well. Similarly, towards the top-left of the graph, we’re running into quantization there as the degree of a node must be a whole number.&lt;/p&gt;
&lt;p&gt;So what are we looking for here? For reasons which will become clear, let’s bring back our random graph from earlier and run the same procedure on it:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;600&quot; height=&quot;371&quot; src=&quot;https://breq.dev/_astro/distribution-with-random.BKljsDlZ_1e8zEN.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;That looks very different — sort of like a bell curve around some single most-popular degree value, giving the network a sense of “scale” or “size.” The 88x31 graph, on the other hand, is &lt;a href=&quot;https://en.wikipedia.org/wiki/Scale-free_network&quot;&gt;&lt;em&gt;scale-free&lt;/em&gt;&lt;/a&gt; in that hubs in the network get larger as the network grows.&lt;/p&gt;
&lt;p&gt;Scale-free networks are more rigorously defined as a network in which the degree distribution follows a &lt;a href=&quot;https://en.wikipedia.org/wiki/Power_law&quot;&gt;&lt;em&gt;power law&lt;/em&gt;&lt;/a&gt;. That blue line in the graph is the power law function that best fits the data. Especially compared to the random graph, it’s pretty clear that our 88x31 graph meets the definition of scale-free.&lt;/p&gt;
&lt;p&gt;What are these hubs in our network? In our case, they’re mostly sites which try to collect as many 88x31 buttons as possible into a single site, like the &lt;a href=&quot;https://neonaut.neocities.org/cyber/88x31&quot;&gt;neonaut collection&lt;/a&gt; (which currently holds the #1 spot).&lt;/p&gt;
&lt;h1 id=&quot;conclusions&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#conclusions&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Conclusions&lt;/h1&gt;
&lt;p&gt;In this post, we looked at an entirely new network dataset through the lens of network science, discovering that it fits the same criteria that characterize social, communication, transport, and biological networks in our world.&lt;/p&gt;
&lt;p&gt;Network science is a field that interests me greatly, but that doesn’t often come up much in my work, so I’m grateful for the chance to work on this project. A lot of key insights about real-world networks from the field come up in unexpected ways.&lt;/p&gt;
&lt;p&gt;We’ll continue to track the 88x31 network over the forseeable future. Maybe we’ll discover an unmapped part of the network and end up with even more data to sort through. If you’d like to follow the technical effort, feel free to watch or engage via the project’s &lt;a href=&quot;https://github.com/NotNite/eightyeightthirtyone&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Of course, you can join the network as well, and it’s as straightforward as it sounds: find a friend with 88x31s on their website, make up your own little 88 pixel by 31 pixel image in your favorite bitmap image editor, and get them to add it to their site along with a link to your website! A few tips:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gimp.org/&quot;&gt;GIMP&lt;/a&gt; is a great editor to get started with — just crank the zoom up to 800% or so depending on your screen size.&lt;/li&gt;
&lt;li&gt;Save as a PNG (or GIF for animated 88x31s) to prevent lossy compression from messing with your pixel art.&lt;/li&gt;
&lt;li&gt;When adding a button to your website, use the CSS &lt;code&gt;image-rendering: pixelated&lt;/code&gt; property to keep your pixel-art goodness from being blurred on high-DPI displays or when zoomed in.&lt;/li&gt;
&lt;li&gt;Look for inspiration in the buttons made by your friends, and don’t be afraid to experiment!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hope to see you on &lt;a href=&quot;https://eightyeightthirty.one/&quot;&gt;eightyeightthirty.one&lt;/a&gt; soon!&lt;/p&gt;</content:encoded></item><item><title>eightyeightthirty.one</title><link>https://breq.dev/projects/eightyeightthirtyone/</link><guid isPermaLink="true">https://breq.dev/projects/eightyeightthirtyone/</guid><description>A snapshot of the social graph of 88x31 badges.</description><pubDate>Tue, 26 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2256&quot; height=&quot;1924&quot; src=&quot;https://breq.dev/_astro/graph.DzIxBemA_ZvQdyX.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;The entire mapped network of 16,000+ pages, as of 2023-12-26.&lt;/p&gt;
&lt;p&gt;This project was a joint effort by myself and a few friends:&lt;/p&gt;
&lt;div class=&quot;flex justify-center&quot; style=&quot;image-rendering: pixelated;&quot;&gt;
&lt;div class=&quot;flex flex-row border-2 border-black dark:border-white p-4 gap-4 rounded-lg&quot;&gt;
  &lt;a href=&quot;https://notnite.com/&quot;&gt;&lt;img src=&quot;https://breq.dev/badges/notnite.png&quot;&gt;&lt;/a&gt;
  &lt;a href=&quot;https://adryd.com/&quot;&gt;&lt;img src=&quot;https://breq.dev/badges/adryd.png&quot;&gt;&lt;/a&gt;
  &lt;a href=&quot;https://breq.dev/&quot;&gt;&lt;img src=&quot;https://breq.dev/badges/breq.png&quot;&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;88x31 buttons are everywhere on the indie web — they’re those tiny buttons on the homepage or footer of sites like mine which link to friends, projects, etc. They’ve been around for decades and have spread all over webpages and forum signatures. However, until now, there has been no way to view the entire network of 88x31 links all at once.&lt;/p&gt;
&lt;p&gt;My friends and I have implemented a scraper which can crawl a page for 88x31 links, a server to manage the queue of sites to crawl, and a web frontend to visualize the graph as a whole.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://notnite.com/&quot;&gt;NotNite&lt;/a&gt; provided the initial idea and implementation, and after &lt;a href=&quot;https://adryd.com/&quot;&gt;adryd&lt;/a&gt; sent me a proof of concept, the three of us and some friends immediately hopped on a call to work out the details.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The implementation consists of three parts: the scraper, the orchestration server, and the frontend.&lt;/p&gt;
&lt;h2 id=&quot;scraper&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#scraper&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Scraper&lt;/h2&gt;
&lt;p&gt;The scraper receives a URL and is responsible for visiting the webpage to look for 88x31s which link to pages. We experimented with a webdriver-based solution (using &lt;a href=&quot;https://pptr.dev/&quot;&gt;Puppeteer&lt;/a&gt;), but ended up switching to static HTML parsing for performance, making the tradeoff that the scraper can’t read client-side-rendered pages.&lt;/p&gt;
&lt;p&gt;The scraper is also responsible for noting any redirects that happen, and for trying to identify canonical URLs for pages, just as a traditional search engine crawler would need to.&lt;/p&gt;
&lt;p&gt;The scraper is written in Rust and keeps no state, so it can be scaled up horizontally as needed.&lt;/p&gt;
&lt;h2 id=&quot;orchestration-server&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#orchestration-server&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Orchestration Server&lt;/h2&gt;
&lt;p&gt;To coordinate the scrapers and provide access control, an orchestration server is used. This server also accepts URLs into the network (adding them to the queue) and produces the graph file.&lt;/p&gt;
&lt;p&gt;The orchestration server is backed by a Redis database — this was chosen to ensure the queue was stored in memory.&lt;/p&gt;
&lt;p&gt;The orchestration server also handles inserting found links into the queue, and correcting records after a redirect is found — both complex processes to handle edge cases that arise when webmasters change things about their site.&lt;/p&gt;
&lt;h2 id=&quot;frontend&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#frontend&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Frontend&lt;/h2&gt;
&lt;p&gt;The frontend is used to display the graph to the user and allow them to navigate it efficiently. It has some functionality for zooming to a particular node and highlighting its links, and it can show the badge images used to link from one site to another.&lt;/p&gt;
&lt;p&gt;We struggled to choose an effective graph library implementation which could render this many nodes on the screen, but eventually settled on &lt;a href=&quot;https://cosmograph.app/&quot;&gt;Cosmograph&lt;/a&gt; as it blew everything else out of the water in terms of performance. The end result still takes time to load the initial graph but feels relatively snappy to navigate even on mobile devices.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;The result is a map of over 16,000 pages, either linking to or linked from another using 88x31s.&lt;/p&gt;
&lt;p&gt;One thing that we didn’t expect to happen was webmasters noticing our user-agent in their access logs and inquiring about our work. Apparently folks aren’t used to scrapers which specifically target 88x31 images! It was very cool to get to explain this project to those who asked about it, and we made sure to clearly identify ourselves and provide opt-out instructions in case any operators disagreed with our mission.&lt;/p&gt;
&lt;p&gt;I also got to finally use everything I learned in a network science course last year to &lt;a href=&quot;https://breq.dev/2023/12/26/88x31-science&quot;&gt;analyze our resulting data&lt;/a&gt;, which was quite fun. TL;DR: our dataset of 88x31 links is similar to other social networks which have been studied in the field!&lt;/p&gt;</content:encoded></item><item><title>In Defense of Really Long Merge Requests</title><link>https://breq.dev/2023/07/27/long-diffs/</link><guid isPermaLink="true">https://breq.dev/2023/07/27/long-diffs/</guid><description>Massive diffs aren&apos;t anything to fear.</description><pubDate>Thu, 27 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Disclaimer: This article may or may not be an application of &lt;a href=&quot;https://meta.wikimedia.org/wiki/Cunningham%27s_Law&quot;&gt;Cunningham’s Law&lt;/a&gt;. I’d love to hear about how other teams work and why people like (or dislike!) their current way of doing things.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I’ve maintained an (albeit small) &lt;a href=&quot;https://github.com/breqdev/flask-discord-interactions/pulls?q=is%3Apr+is%3Aclosed&quot;&gt;open-source library&lt;/a&gt;, led software development on a &lt;a href=&quot;https://www.northeasternrover.com/&quot;&gt;robotics team of &gt;40 people&lt;/a&gt;, and worked on a professional software team at co-op. Through this, I’ve gotten to experience a range of code review styles and merge request etiquette. A common piece of wisdom I’ve seen tends to be: “keep your merge requests small, so they are easily reviewable.” Conventional wisdom states that longer merge requests are more difficult to review — obviously this is true, since there are more changes to look through. I’d like to argue, however, that &lt;em&gt;in the long run&lt;/em&gt;, writing longer merge requests can save time and effort for a team.&lt;/p&gt;
&lt;h1 id=&quot;breaking-features-down-is-burning-developer-effort&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#breaking-features-down-is-burning-developer-effort&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Breaking features down is burning developer effort&lt;/h1&gt;
&lt;p&gt;Suppose you’ve set out to implement a feature, and you’ve ended up with a much larger implementation than you expected. Should you split that implementation into smaller merge requests to make life easier for your reviewers?&lt;/p&gt;
&lt;p&gt;What makes breaking up merge requests tricky is that &lt;strong&gt;you’ll need to verify that the state of the repository in between your two merge requests is valid.&lt;/strong&gt; You need to create an intermediate version, then test that intermediate version. Semantically, this “in-between” version might make no sense, since it contains a partially-completed feature.&lt;/p&gt;
&lt;p&gt;If you’re using a tool like Rust’s &lt;a href=&quot;https://doc.rust-lang.org/nightly/clippy/&quot;&gt;clippy&lt;/a&gt; to analyze your code, the intermediate version might need to be littered with &lt;code&gt;#[allow(dead_code)]&lt;/code&gt; or your language’s equivalent in order to pass CI. This clutters the commit history and provides no real value. And what if you accidentally leave in one of those &lt;code&gt;#[allow(dead_code)]&lt;/code&gt; functions later?&lt;/p&gt;
&lt;p&gt;Splitting a merge request into more than two parts makes this even more complicated. In many cases, it just isn’t practical to break a single feature into multiple semantically reasonable and CI-passing intermediate steps.&lt;/p&gt;
&lt;h1 id=&quot;more-frequent-code-reviews-means-more-round-trips-and-greatly-reduced-speed&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#more-frequent-code-reviews-means-more-round-trips-and-greatly-reduced-speed&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;More frequent code reviews means more “round trips” and greatly reduced speed&lt;/h1&gt;
&lt;p&gt;When you submit a merge request, you’re essentially handing things off to your reviewer. A good reviewer will get back to you in 24 hours, but someone who’s busy with other work might take even longer. In this intermediate time, what do you do? In most cases, you’ll end up switching to another project, losing steam on the feature you were working on. Only when they get back to you can you resume work on the feature. The more of these “round trips” you have to deal with, the longer it will take you to implement useful features.&lt;/p&gt;
&lt;p&gt;The good news is that there are ways to gather feedback from others in the middle of implementing a feature that don’t slow you down! You can set up a 1-on-1 to talk through design decisions, exchange UML drawings, pair program, or just Slack your teammate a link to your branch. They can then respond at their own pace, without blocking you from doing your work.&lt;/p&gt;
&lt;h1 id=&quot;if-merge-requests-queue-up-addressing-concerns-becomes-nontrivial&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#if-merge-requests-queue-up-addressing-concerns-becomes-nontrivial&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;If merge requests queue up, addressing concerns becomes nontrivial&lt;/h1&gt;
&lt;p&gt;If you’re impatient, you might not wait for one of your merge requests to be approved before starting work on the next. After all, they’re all part of one feature, so it makes sense to tackle them sequentially. So you start your &lt;code&gt;feature-2&lt;/code&gt; branch before your &lt;code&gt;feature-1&lt;/code&gt; branch is merged in.&lt;/p&gt;
&lt;p&gt;In order to pull this off, however, you’ll need to branch &lt;code&gt;feature-2&lt;/code&gt; &lt;em&gt;off of&lt;/em&gt; &lt;code&gt;feature-1&lt;/code&gt;, since the changes in &lt;code&gt;feature-1&lt;/code&gt; aren’t in &lt;code&gt;main&lt;/code&gt; yet. This works fine for now. And if your code is good, it’s not a problem, since you’ll just merge &lt;code&gt;feature-1&lt;/code&gt; into &lt;code&gt;main&lt;/code&gt; first, then merge &lt;code&gt;feature-2&lt;/code&gt; in after.&lt;/p&gt;
&lt;p&gt;This falls apart as soon as your reviewer points out an issue with &lt;code&gt;feature-1&lt;/code&gt;. You switch to that branch and commit your fixes. Then, while waiting to hear back from your reviewer, you go back to developing on &lt;code&gt;feature-2&lt;/code&gt;. But &lt;code&gt;feature-2&lt;/code&gt; doesn’t have those changes, meaning you’ll have to either rebase it onto &lt;code&gt;feature-1&lt;/code&gt; (which is annoying if any teammates also checked out that branch) or merge &lt;code&gt;feature-1&lt;/code&gt;’s most recent commit into it (which clutters up history). Then your reviewer requests more changes, and you need to do the song and dance once again. You probably can’t just merge in all of your changes into &lt;code&gt;feature-2&lt;/code&gt; once &lt;code&gt;feature-1&lt;/code&gt; gets merged, since they’re so tightly coupled.&lt;/p&gt;
&lt;p&gt;This gets quadratically worse if you have a longer chain of merge requests. I’ve seen situations like this up to four layers deep, where each change to the oldest MR require rebasing every other MR in sequence. This is an awful experience for everyone involved, and it leads to comments on merge requests like “Fixed this in [later merge request], please approve this one now,” which is definitely not the way things should work.&lt;/p&gt;
&lt;h1 id=&quot;reviewers-need-to-consider-a-certain-amount-of-context-regardless&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#reviewers-need-to-consider-a-certain-amount-of-context-regardless&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Reviewers need to consider a certain amount of context regardless&lt;/h1&gt;
&lt;p&gt;When you’re reviewing code changes, you aren’t just thinking about the code itself. You need to consider everything else in the system that the code interacts with.&lt;/p&gt;
&lt;p&gt;Suppose you’re incrementally replacing a control stack for a device written in ROS. Conceptually, the new and old implementations will be quite different.&lt;/p&gt;
&lt;p&gt;You could go about this by rewriting each node in turn, each as its own merge request. You’ll need to plan an order to migrate nodes in that allows you to reach the “target” system architecture, but you’ll also need to not break the existing system along the way, since the system should be functional at each point in between the merge requests. If you want to rename certain topics shared between nodes, or change the message type passed between nodes, you’ll need to carefully plan when you do that as well.&lt;/p&gt;
&lt;p&gt;Then, your reviewers will need to consider each change in the context of both the existing system and the future/new system. Again, you can’t break things in between merge requests, but you also want to make sure you’re architecting things properly for the final system.&lt;/p&gt;
&lt;p&gt;Or, you can just replace the old system with a new system in a single, atomic merge request, allowing your reviewers to focus exclusively on reviewing the new system.&lt;/p&gt;
&lt;p&gt;For a different example: consider implementing a program that bridges a serial, I2C, or SPI connection and a Redis pub/sub channel, for instance forwarding messages from serial into Redis and vice versa. Suppose you want your program to handle several data types and have a consistent interface on each hardware interface.&lt;/p&gt;
&lt;p&gt;You could break this feature into separate merge requests for each interface. Then, when your reviewers went to review &lt;em&gt;each&lt;/em&gt; merge request, they would need to understand the management of the Redis connection, the datatype system used throughout the codebase, the differences in protocol between each of the planned hardware interfaces, and the handling of the specific hardware interface actually implemented in the MR.&lt;/p&gt;
&lt;p&gt;This isn’t too bad if these merge requests are reviewed one after the other, but what if a week or more goes by between each of them? Your reviewer will need to remember each of these things, every time, ultimately leading to spending more time on review compared to just reviewing everything all at once.&lt;/p&gt;
&lt;h1 id=&quot;long-diffs-should-take-longer-to-review&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#long-diffs-should-take-longer-to-review&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Long diffs &lt;em&gt;should&lt;/em&gt; take longer to review&lt;/h1&gt;
&lt;p&gt;There’s a common refrain that “you can’t possibly catch everything in a patch of more than X lines.”&lt;/p&gt;
&lt;p&gt;Consider this: would you rather read one chapter of a book every week or two, or spend a few days reading as much as you can? Which would allow you to better follow the story? Keep track of the characters? Understand the book at a deeper level?&lt;/p&gt;
&lt;p&gt;I see no fundamental reason why a reviewer would have a lower success rate at spotting issues per line of code for a larger diff as opposed to a smaller one. It will require more time to read and understand, and it will require a reviewer to potentially be more deliberate in their review, but it ultimately leads to the same quality assurance with substantially reduced effort for the development team.&lt;/p&gt;</content:encoded></item><item><title>So, You Want To Stream Lots Of USB Cameras At Once</title><link>https://breq.dev/2023/06/21/cameras/</link><guid isPermaLink="true">https://breq.dev/2023/06/21/cameras/</guid><description>Simple, right? Here&apos;s everything that will go wrong.</description><pubDate>Wed, 21 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Let’s say you’re building a live camera feed for something. Maybe it’s a 3D printer, or an RC car, or a &lt;a href=&quot;https://www.northeasternrover.com/&quot;&gt;Mars Rover analogue&lt;/a&gt; (website hasn’t been updated in years). Here’s a pretty typical requirements list:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Video feed sent over an IP network&lt;/li&gt;
&lt;li&gt;&amp;#x3C;500ms latency&lt;/li&gt;
&lt;li&gt;Reasonably efficient encoding (e.g. H.264)&lt;/li&gt;
&lt;li&gt;640x480 resolution, 30 fps&lt;/li&gt;
&lt;li&gt;Recover from network failures&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With one stream, you’re in pretty good shape! Just plug in any off-the-shelf USB camera, run a quick GStreamer command on both ends, and boom, you’re good to go. But as you add more cameras into the mix, you’ll run into some hiccups at seemingly every point in the pipeline.&lt;/p&gt;
&lt;p&gt;This blog post is the culmination of 2 years of work into camera streaming pipelines on NU ROVER, Northeastern University’s team for the &lt;a href=&quot;https://urc.marssociety.org/&quot;&gt;University Rover Challenge&lt;/a&gt;. Over that time, we’ve evolved from an unreliable six-camera setup requiring four separate devices to an efficient, easy-to-use, reliable system encompassing 14 cameras on a single computer. This post will summarize the experimentation we did, the decisions we made, and the lessons we learned, starting with the individual camera modules themselves and working up through the pipeline all the way to how the feed is displayed.&lt;/p&gt;
&lt;h1 id=&quot;cameras&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#cameras&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Cameras&lt;/h1&gt;
&lt;p&gt;A camera module connects some image sensor to some interface. The choice of image sensor doesn’t impact much; NU ROVER chooses camera modules based purely on FOV and resolution. The interface, however, greatly affects what hardware you can use for streaming.&lt;/p&gt;
&lt;h2 id=&quot;csi&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#csi&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;CSI&lt;/h2&gt;
&lt;p&gt;Camera Serial Interface (CSI), sometimes called “MIPI,” is the most barebones protocol for connecting a camera: the protocol is specifically designed for cameras and connected directly to the CPU on boards like the Raspberry Pi. A good example of this type is the Raspberry Pi Camera, but other manufacturers supply various cameras with different FOVs and sensors.&lt;/p&gt;
&lt;p&gt;Pros:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Direct connection with processor, very low overhead&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Limited to number of CSI lanes provided by platform (e.g. 1 for Raspberry Pi, 2 for Jetson Nano, 6 for Jetson Orin)&lt;/li&gt;
&lt;li&gt;Spec limits cable length to 30cm (although it usually works up to 2m)&lt;/li&gt;
&lt;li&gt;Requires software tailored to the platform (e.g. &lt;a href=&quot;https://www.raspberrypi.org/app/uploads/2013/07/RaspiCam-Documentation.pdf&quot;&gt;&lt;code&gt;raspivid&lt;/code&gt;&lt;/a&gt;), which may complicate integration with streaming software&lt;/li&gt;
&lt;li&gt;More fragile cables than USB&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the past, we used multiple devices (2 Raspberry Pis, a Jetson Nano, and a Jetson TX2) to connect many CSI cameras into our system. This dramatically increased complexity and required installation of a network switch, taking up precious space inside the rover. Our new system does not make use of CSI cameras.&lt;/p&gt;
&lt;h2 id=&quot;ip&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#ip&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;IP&lt;/h2&gt;
&lt;p&gt;IP cameras are often used as part of home security systems, making them readily available. They directly connect using Ethernet, removing the need for a separate device for encoding. Our team purchased a few, and I spent some time testing them, but I ruled them out pretty quickly. They’re physically much harder to mount and integrate into a system, and since they’re typically designed for recording and not live viewing, they often have very high latency.&lt;/p&gt;
&lt;p&gt;Pros:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Minimal configuration needed&lt;/li&gt;
&lt;li&gt;Designed to scale up to &gt;10 camera systems&lt;/li&gt;
&lt;li&gt;Durable and weatherproof&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cameras are quite bulky and thus harder to find mounting points for&lt;/li&gt;
&lt;li&gt;Cables are much larger and difficult to route&lt;/li&gt;
&lt;li&gt;Requires using a (potentially very large) network switch to connect multiple&lt;/li&gt;
&lt;li&gt;Provide limited or no encoder configuration (e.g. no way to adjust stream bitrate)&lt;/li&gt;
&lt;li&gt;High latency (~1000-2000ms)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you’re working on a physically large, stationary, or spread-out system, IP cameras might be worth considering. Otherwise, the added bulk is a substantial drawback.&lt;/p&gt;
&lt;h2 id=&quot;analog-video&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#analog-video&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Analog Video&lt;/h2&gt;
&lt;p&gt;This year, our team partnered up with NUAV, Northeastern’s unmanned aerial vehicles organization, to utilize a drone in part of the autonomous challenge. Their camera feed (used only during emergency teleoperation) was a 5.8 GHz off-the-shelf system common in FPV (first-person view) drone operation. These systems use an analog video signal to send video to the operator’s goggles. While analog video is tempting as a self-contained system, it doesn’t scale effectively to a multi-camera setup.&lt;/p&gt;
&lt;p&gt;Pros:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Standalone system&lt;/li&gt;
&lt;li&gt;Low latency&lt;/li&gt;
&lt;li&gt;Graceful signal degradation if interference exists&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Requires a dedicated antenna, receiver, and display to view each feed&lt;/li&gt;
&lt;li&gt;Each video feed requires its own hardware&lt;/li&gt;
&lt;li&gt;Almost always fixed to 5.8 GHz (shorter range compared to 2.4 GHz WiFi or 900 MHz IP radios)&lt;/li&gt;
&lt;li&gt;Limited number of channels/cameras (8)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We considered installing an analog video camera to use if our primary network link dropped out, but given the characteristics of the 5.8 GHz band, there are few situations in which this would be helpful. (If we can get a 5.8 GHz signal to the rover, we can almost definitely get a 2.4 GHz signal to it as well.)&lt;/p&gt;
&lt;h2 id=&quot;usb&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#usb&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;USB&lt;/h2&gt;
&lt;p&gt;This brings us to USB cameras, arguably the most common type in consumer use today. These seem like an obvious choice, but there are a few complications when you scale beyond one or two cameras (which we’ll get into).&lt;/p&gt;
&lt;p&gt;Pros:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cable and connector are durable but not too bulky&lt;/li&gt;
&lt;li&gt;Standard software support across platforms&lt;/li&gt;
&lt;li&gt;Small modules available with standardized mounting holes from Arducam, ELP, etc.&lt;/li&gt;
&lt;li&gt;&lt;del&gt;Unlimited number of cameras on each computer&lt;/del&gt; (we’ll get to this…)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Some overhead compared with CSI&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s no surprise USB was our first choice when we began developing the system. Even if you rely on CSI cameras for some systems, you’re unlikely to be able to build a low-cost and practical system that incorporates 10 or more of them, meaning you’d have to mix in some USB cameras as well.&lt;/p&gt;
&lt;h1 id=&quot;usb-bandwidth&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#usb-bandwidth&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;USB Bandwidth&lt;/h1&gt;
&lt;p&gt;We’re all used to just plugging in USB devices arbitrarily and having everything work. Cameras, however, tend to push the USB 2.0 connection to its limit, and using several of them on one computer can be surprisingly difficult.&lt;/p&gt;
&lt;h2 id=&quot;a-bit-of-bandwidth-math&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#a-bit-of-bandwidth-math&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;A bit of bandwidth math&lt;/h2&gt;
&lt;p&gt;Consider the bandwidth requirement of a typical video stream: 640x480 pixels, 30 fps.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Quick note: If you’ve worked with images, you might assume we need 8 bits per channel to represent each pixel in the frame. This isn’t quite true — we can save a little extra space with some smart encoding. Most raw video from USB cameras is in “YUYV” format, and contains three values: Y (luminance, or “brightness”), U (color), and V (also color). The eye is more sensitive to small variations in luminance than to similar variations in color, so we can save space by using 8 bits for luminance and 4 bits for each of the color signals, giving a total of 16 bits per pixel per frame.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The requirement for a single frame is thus:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;640&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;480&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;16&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;4915200&lt;/mn&gt;&lt;mtext&gt; bits&lt;/mtext&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;640 \times 480 \times 16 = 4915200 \text{ bits}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;640&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;480&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;4915200&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord&quot;&gt; bits&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;And the requirement for a single second of video is:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;4915200&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;30&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;147456000&lt;/mn&gt;&lt;mtext&gt; bit/s&lt;/mtext&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mn&gt;147&lt;/mn&gt;&lt;mtext&gt; Mbit/s&lt;/mtext&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mn&gt;18.4&lt;/mn&gt;&lt;mtext&gt; MByte/s&lt;/mtext&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;4915200 \times 30 = 147456000 \text{ bit/s} \approx 147 \text{ Mbit/s} \approx 18.4 \text{ MByte/s}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;4915200&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;147456000&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord&quot;&gt; bit/s&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;147&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord&quot;&gt; Mbit/s&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;18.4&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord&quot;&gt; MByte/s&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;The theoretical maximum speed of USB 2.0 is 480 Mbit/s, and subtracting &lt;a href=&quot;https://microchipdeveloper.com/usb:high-speed&quot;&gt;signal overhead&lt;/a&gt;, we’re only left with &lt;strong&gt;53.2 MByte/s&lt;/strong&gt;. For a single camera, this is fine! We’ve got plenty of bandwidth left over.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;A diagram showing a camera connected to a USB controller, showing only using a small portion of the total link capacity&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;154&quot; height=&quot;224&quot; src=&quot;https://breq.dev/_astro/01-single.etLwxpgh_Z2frLAr.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;What happens when we add multiple cameras? It depends. Let’s first consider the case that the cameras are on different USB &lt;em&gt;controllers&lt;/em&gt; from each other — we’ll go over what this means in a second. This is also fine.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Three parallel copies of the prior diagram&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;434&quot; height=&quot;224&quot; src=&quot;https://breq.dev/_astro/02-parallel.CG-Bz5lh_OH3VS.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now what if we try to put multiple cameras on a single USB hub? Here’s where we start to run into problems.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;A diagram showing three cameras connected to one USB controller through a hub, where the link between the hub and the controller is oversaturated&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;434&quot; height=&quot;374&quot; src=&quot;https://breq.dev/_astro/03-hub.BgHF21iV_Z2qQbN5.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;In a setup like this, two of the cameras will work fine, but the third will not work. Specifically, you’ll be able to open all three device files simultaneously, but you’ll only receive video frames from two of them. The third camera will not raise an error, which can make troubleshooting annoying.&lt;/p&gt;
&lt;p&gt;If this seems weird for a reasonable USB setup to fail like this, that’s because it is. Most devices don’t come anywhere near to reaching the USB bandwidth limit. And if they do, they’ll typically handle it gracefully: your SSD might copy files over a little bit slower, or your printer might take a bit longer to load the document you sent it.&lt;/p&gt;
&lt;p&gt;With a USB camera, though, there’s no good way to handle a lack of bandwidth: it doesn’t have the memory to delay frames and send them later, it can’t send a smaller-sized image since the software expects a specific resolution, and there’s no way for it to coordinate with other cameras to share the bandwidth by slowing down framerate. Thus, you get nothing.&lt;/p&gt;
&lt;p&gt;Let’s take a step back and look at what this means in practice. A USB &lt;em&gt;controller&lt;/em&gt; is the “root” of the tree of USB devices. It’s a chip that connects the USB bus to the CPU, either directly or via a protocol like PCIe. In practice, most computers only have one or two controllers, then use internal USB hubs to provide more physical USB ports.&lt;/p&gt;
&lt;p&gt;For instance, a Raspberry Pi 3 has four physical USB ports but only a single internal USB controller, meaning all of the ports share bandwidth. As a result, you’re unlikely to get more than two USB cameras working in YUYV mode at this resolution simultaneously on the Pi.&lt;/p&gt;
&lt;h2 id=&quot;usb-30&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#usb-30&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;USB 3.0&lt;/h2&gt;
&lt;p&gt;However, most modern devices have USB 3.0 ports. Can those help us out? USB 3.0 has a theoretical maximum rate of &lt;strong&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/USB#USB_3.x&quot;&gt;500 Mbyte/s&lt;/a&gt;&lt;/strong&gt;, so we’re good, right?&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;An incorrect diagram showing the oversaturated link &amp;quot;upgraded&amp;quot; to USB 3.0 by replacing the hub and controller with USB 3.0 variants&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;434&quot; height=&quot;374&quot; src=&quot;https://breq.dev/_astro/04-usb3-incorrect.CXR32gBu_Z2qQbN5.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Unfortunately, &lt;strong&gt;this is not how this works.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;USB 2.0 uses a single differential pair of wires for signaling. USB 3.0 adds extra wires to the cable and connector, for a total of 3 differential pairs. The two additional pairs are used for “SuperSpeed” signaling (the fast data rates), but the original pair is still just a regular USB 2.0 connection. USB 3.0 traffic is handled completely separately from USB 2.0 traffic.&lt;/p&gt;
&lt;p&gt;In essence, &lt;em&gt;a “USB 3.0 cable” is a USB 3.0 cable and a USB 2.0 cable in the same housing&lt;/em&gt;. Consequently, &lt;em&gt;a “USB 3.0 hub” is a USB 3.0 hub and a USB 2.0 hub in the same box, wired up to different pins in the same connectors&lt;/em&gt;. And you guessed it, &lt;em&gt;a “USB 3.0 controller” is just a USB 3.0 controller and a USB 2.0 controller in the same chip&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Here’s the output of &lt;code&gt;lsusb -t&lt;/code&gt; on a Raspberry Pi 4:&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see that despite the Pi 4 having a single USB controller chip, it appears in Linux as two entirely separate USB buses — one for USB 2.0 (the “480M” is the speed of the port in bits) and the other for USB 3.0 (listed as “5000M”).&lt;/p&gt;
&lt;p&gt;Here’s a diagram that more accurately captures what’s going on:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;the diagram showing the oversaturated link, but with a separate USB 3.0 link off to the side with zero utilization&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;444&quot; height=&quot;384&quot; src=&quot;https://breq.dev/_astro/05-usb3-correct.DnevQXN7_BejIh.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Since the cameras are all USB 2.0, all of the data they send stays on the USB 2.0 pair all the way to the controller. (USB 3.0 cameras exist, but are expensive and rare outside of industrial settings.)&lt;/p&gt;
&lt;h2 id=&quot;mjpg-encoding&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#mjpg-encoding&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;MJPG encoding&lt;/h2&gt;
&lt;p&gt;Your webcam probably runs at much higher than 640x480p. The popular Logitech C920, for instance, supports 1080p video at 30 fps and runs over USB 2.0, which seems like it’d use up more bandwidth than we have, right? Something doesn’t add up.&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;1920&lt;/mn&gt;&lt;mtext&gt; pixels&lt;/mtext&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;1080&lt;/mn&gt;&lt;mtext&gt; pixels&lt;/mtext&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;16&lt;/mn&gt;&lt;mtext&gt; bits&lt;/mtext&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;30&lt;/mn&gt;&lt;mtext&gt; fps&lt;/mtext&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mn&gt;995.3&lt;/mn&gt;&lt;mtext&gt; Mbit/s&lt;/mtext&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mn&gt;124.4&lt;/mn&gt;&lt;mtext&gt; Mbyte/s&lt;/mtext&gt;&lt;mo&gt;&gt;&lt;/mo&gt;&lt;mn&gt;53.2&lt;/mn&gt;&lt;mtext&gt; Mbyte/s&lt;/mtext&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;1920 \text{ pixels} \times 1080 \text{ pixels} \times 16 \text{ bits} \times 30 \text{ fps} \approx 995.3 \text{ Mbit/s} \approx 124.4 \text{ Mbyte/s} \gt 53.2 \text{ Mbyte/s}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8889em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1920&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord&quot;&gt; pixels&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8889em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1080&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord&quot;&gt; pixels&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7778em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord&quot;&gt; bits&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8889em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord&quot;&gt; fps&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;995.3&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord&quot;&gt; Mbit/s&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;124.4&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord&quot;&gt; Mbyte/s&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;53.2&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord&quot;&gt; Mbyte/s&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;These cameras support higher resolutions and framerates by compressing frames before sending them to the computer, using a technique known as MJPG (motion JPEG). Frames are captured by the camera, compressed using the JPEG algorithm, and then sent along the wire to the computer, at which point they can be decompressed.&lt;/p&gt;
&lt;p&gt;Different cameras will use different compression ratios depending on the resolution and framerate, meaning we don’t have hard numbers to go off of. The improvement from motion JPEG in a multi-camera setup is unfortunately not that great: compression ratios are set assuming a given camera is the only device on the bus, so the bandwidth reduction at smaller resolutions is much less significant. In practice, I’ve found that up to 3 cameras can coexist on a single bus at this resolution and framerate, meaning it’s a pretty minor improvement.&lt;/p&gt;
&lt;h2 id=&quot;your-toolbox&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#your-toolbox&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Your Toolbox&lt;/h2&gt;
&lt;p&gt;Here are the tools you have to try to solve the USB bandwidth problem:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Add more devices, and split up the cameras between them.&lt;/strong&gt; NU ROVER did this initially, but it complicates your entire system quite a bit — you now have to deal with a network switch, plus however many extra devices you brought in.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Find a way to add more USB controllers to a particular device.&lt;/strong&gt; When we upgraded our systems to the Jetson Orin, we added a USB expansion card into the PCIe slot. In particular, we chose a &lt;a href=&quot;https://www.microcenter.com/product/439934/vantec-quad-chip-4-port-dedicated-5gbps-usb-30-pcie-host-card&quot;&gt;“Quad Chip” model&lt;/a&gt; which contained four separate USB controllers. These are popular among VR enthusiasts — it turns out connecting many stationary VR tracking towers leads to similar bandwidth issues as USB cameras.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Switch to the MJPG mode instead of the YUYV one.&lt;/strong&gt; This adds a bit of latency since the device now has to decode the MJPG image, but the bandwidth improvements are often significant enough to make it worthwhile. Plus, the MJPG modes are typically more similar across camera models — some cameras can be picky about what framerates they accept for YUYV streaming, for instance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plan which camera connects to which controller carefully.&lt;/strong&gt; If you plan on dynamically starting and stopping streams, and you know you’ll never want two specific cameras running simultaneously, you can put them on the same controller and they’ll never have to fight over bandwidth. Conversely, if there’s a set of cameras you plan on using all together, make sure not too many of them are on the same controller.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apply some driver tweaks to squeeze out a little extra bandwidth.&lt;/strong&gt; The Linux UVC driver provides a mode (&lt;a href=&quot;https://stackoverflow.com/a/25619508&quot;&gt;&lt;code&gt;UVC_QUIRKS_FIX_BANDWIDTH&lt;/code&gt;&lt;/a&gt;) that calculates the bandwidth requirements for each device itself, which can sometimes be more accurate than what the camera reports.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;encoding&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#encoding&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Encoding&lt;/h1&gt;
&lt;p&gt;You’ve gotten an image from the camera. Now what?&lt;/p&gt;
&lt;p&gt;You’ll probably want to use &lt;a href=&quot;https://gstreamer.freedesktop.org/&quot;&gt;GStreamer&lt;/a&gt; to link together most of your pipeline. GStreamer is a framework that allows you to chain together different video elements, such as sources, encoders, payloaders, and decoders, into a pipeline that is executed all at once. The concepts in this post aren’t GStreamer-exclusive, and I won’t be providing many “ready-to-go” pipelines since they vary based on hardware encoding support and system, but I will at times assume that you’re linking together your source, encoder, and payloader using GStreamer.&lt;/p&gt;
&lt;p&gt;The most common codec for video compression is H.264, which is generally good enough for most purposes. Other codecs like H.265, AV1, and VP9 can offer lower bandwidth, but the benefits are minor at smaller video resolutions. The most important factor is hardware encoding and decoding support; if the encoding is handled by the CPU, it may not keep up with many video streams simultaneously, especially on lower-end devices.&lt;/p&gt;
&lt;p&gt;The most important factors to tune are bandwidth and keyframe interval. &lt;strong&gt;Bandwidth&lt;/strong&gt;, in this case, is the amount of video data sent over the network per second. If your bandwidth is set too high, you’ll start to see artifacts as packets are dropped whenever your network link slows down (for instance, if you have a wireless link and your robot drives behind a wall). If it’s set too low, your video quality will suffer and your feed will be full of compression artifacts. If you’re operating over a wired Ethernet connection, you can probably divide the bandwidth of the Ethernet link by however many streams you plan to have simultaneously, but take into account any other network traffic on the machine, too. NU ROVER uses a bandwidth of 1 Mbps for each 480p stream.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Keyframe interval&lt;/strong&gt;, also known as “I-frame interval,” refers to the time in between I-frames in the video signal. H.264 encoded video consists of I-frames (frames which contain a complete image), P-frames (which reference previous frames), and B-frames (which can reference future frames and are only relevant in the context of a pre-recorded video). The purpose of P-frames is to improve the video compression by not sending the same data in every frame — if your camera feed is mostly a still image, it is wasteful to send that entire image on every frame when you can instead only send the specific areas of the image which changed.&lt;/p&gt;
&lt;p&gt;If you have too many I-frames, you will devote too much of your bandwidth to them and have worse picture quality as a result. However, if you have too few, you may have periods where you can’t see the entire image if an I-frame gets dropped due to network conditions. This usually manifests itself as a gray screen in which moving areas gradually return to color while motionless areas remain gray until the next I-frame. NU ROVER uses 1 keyframe every 30 seconds.&lt;/p&gt;
&lt;h1 id=&quot;protocols&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#protocols&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Protocols&lt;/h1&gt;
&lt;p&gt;Now that you have your encoded video feed, you’ll need to find a way to send it over the network. There are a few existing protocols that can help you here. Of course, you’ll need to use the same protocol on both sides of the connection.&lt;/p&gt;
&lt;h2 id=&quot;motion-jpeg&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motion-jpeg&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motion JPEG&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Motion_JPEG&quot;&gt;Motion JPEG&lt;/a&gt;, also known as MJPG, also works for sending video over a network, frame by frame, usually over HTTP. Each frame is compressed individually, making it one of the simplest protocols. However, because there’s no inter-frame compression, this protocol uses much more bandwidth to stream video at a given quality than others.&lt;/p&gt;
&lt;h2 id=&quot;http-live-streaming&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#http-live-streaming&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;HTTP Live Streaming&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/HTTP_Live_Streaming&quot;&gt;HTTP Live Streaming&lt;/a&gt; (HLS) is a protocol used to stream H.264 videos over the internet. It works by dividing the video into chunks, then serving each chunk in turn using HTTP. This chunking process introduces a high amount of latency, making HLS unsuitable for a low-latency feed.&lt;/p&gt;
&lt;h2 id=&quot;real-time-transport-protocol&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#real-time-transport-protocol&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Real-time Transport Protocol&lt;/h2&gt;
&lt;p&gt;Real-time Transport Protocol (RTP) is one of the simplest ways to send video over a network. It sends data over UDP, meaning network errors and dropped packets are not corrected like they would be with a TCP-based protocol. This is ideal for low-latency applications — if one frame isn’t received correctly, you don’t want to spend time retransmitting it when you could instead be transmitting the next frame. However, RTP could be a poor choice for streams where quality is more important than latency.&lt;/p&gt;
&lt;p&gt;RTP works by payloading data into packets and sending them to a predefined UDP host and port. This payloading process will look different depending on the codec you choose — for now, let’s assume we’re using H.264. GStreamer provides the &lt;code&gt;rtph264pay&lt;/code&gt; plugin to payload H.264 data, as well as the &lt;code&gt;rtph264depay&lt;/code&gt; plugin to depayload it on the other end.&lt;/p&gt;
&lt;p&gt;For example, let’s assume the video server is on &lt;code&gt;192.168.1.10&lt;/code&gt;, the client is on &lt;code&gt;192.168.1.11&lt;/code&gt;, and the video stream should be on port &lt;code&gt;9090&lt;/code&gt;. We can set up an RTP stream by running this GStreamer pipeline on the server:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;gst-launch-1.0 videotestsrc &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; video/x-raw,rate&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;,width&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;640&lt;/span&gt;,height&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;480&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; x264enc &lt;span class=&quot;token assign-left variable&quot;&gt;tune&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;zerolatency &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; rtph264pay &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; udpsink &lt;span class=&quot;token assign-left variable&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;192.168&lt;/span&gt;.1.11 &lt;span class=&quot;token assign-left variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9090&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;videotestsrc&lt;/code&gt; is just a generic test video source (color bars)&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;video/x-raw&lt;/code&gt; part forces the output of &lt;code&gt;videotestsrc&lt;/code&gt; to have the specified resolution and framerate&lt;/li&gt;
&lt;li&gt;&lt;code&gt;x264enc&lt;/code&gt; is the encoder (which runs on the CPU — something to avoid)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rtph264pay&lt;/code&gt; payloads the stream for RTP&lt;/li&gt;
&lt;li&gt;&lt;code&gt;udpsink&lt;/code&gt; sends those payloaded packets over the network to the specified host and port (our client)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then, on our client, we can run this to decode the stream:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;gst-launch-1.0 udpsrc &lt;span class=&quot;token assign-left variable&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9090&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; application/x-rtp,payload&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;96&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; rtph264depay &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; avdec_h264 &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; autovideosink
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;udpsrc&lt;/code&gt; reads incoming packets on a particular UDP port&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;application/x-rtp&lt;/code&gt; part says that those packets should be interpreted as an RTP stream&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rtph264depay&lt;/code&gt; turns the packets into a raw H.264 stream&lt;/li&gt;
&lt;li&gt;&lt;code&gt;avdec_h264&lt;/code&gt; decodes the H.264 stream (on the CPU)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;autovideosink&lt;/code&gt; displays the stream&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To scale this to multiple camera feeds, we just need to choose different UDP ports for each feed. There are plenty of available port numbers for this.&lt;/p&gt;
&lt;h2 id=&quot;real-time-streaming-protocol&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#real-time-streaming-protocol&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Real Time Streaming Protocol&lt;/h2&gt;
&lt;p&gt;The solution above using RTP leaves a few things to be desired:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Having to manually assign port numbers is annoying&lt;/li&gt;
&lt;li&gt;Starting or stopping streams must be done by manually launching or killing processes on the server&lt;/li&gt;
&lt;li&gt;RTP (in mode 96) doesn’t work with VLC or other standard media players&lt;/li&gt;
&lt;li&gt;The server must know the IP address of the client, not the other way around&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol&quot;&gt;Real Time Streaming Protocol&lt;/a&gt;, or RTSP, is a protocol built on top of RTP which can solve these shortcomings. RTSP at first behaves much like an HTTP server, and RTSP URLs look similar to HTTP URLs.&lt;/p&gt;
&lt;p&gt;Let’s look at what happens when a client tries to load &lt;code&gt;rtsp://192.168.1.10/video&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The client opens a connection to &lt;code&gt;192.168.1.10&lt;/code&gt; on port &lt;code&gt;554&lt;/code&gt; (the default RTSP port)&lt;/li&gt;
&lt;li&gt;The client sends an &lt;code&gt;OPTIONS&lt;/code&gt; request to the server for &lt;code&gt;/video&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;The server responds with a list of request types accepted (usually &lt;code&gt;OPTIONS&lt;/code&gt;, &lt;code&gt;DESCRIBE&lt;/code&gt;, &lt;code&gt;PLAY&lt;/code&gt;, &lt;code&gt;TEARDOWN&lt;/code&gt;…)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The client sends a &lt;code&gt;DESCRIBE&lt;/code&gt; request for &lt;code&gt;/video&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;The server responds with a list of available video formats&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The client sends a &lt;code&gt;SETUP&lt;/code&gt; request for &lt;code&gt;/video&lt;/code&gt;, and provides a pair of open ports: an even-numbered port for receiving video data, and an odd-numbered port for receiving UDP control signals using RTCP (RTP Control Protocol).
&lt;ul&gt;
&lt;li&gt;The server responds with its own pair of ports&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The client sends a &lt;code&gt;PLAY&lt;/code&gt; request for &lt;code&gt;/video&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;The server begins sending video data to the client on the specified ports&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The client sends a &lt;code&gt;TEARDOWN&lt;/code&gt; request for &lt;code&gt;/video&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;The server stops sending video data to the client&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;RTSP provides a few other methods, including &lt;code&gt;PAUSE&lt;/code&gt;, which can be useful for interruptable streams.&lt;/p&gt;
&lt;p&gt;To play around with RTSP, you can use the &lt;a href=&quot;https://github.com/sfalexrog/gst-rtsp-launch&quot;&gt;&lt;code&gt;gst-rtsp-launch&lt;/code&gt;&lt;/a&gt; project:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;gst-rtsp-launch &lt;span class=&quot;token string&quot;&gt;&quot;( videotestsrc ! video/x-raw,rate=30,width=640,height=480 ! x264enc tune=zerolatency ! rtph264pay pt=96 name=pay0 )&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, connect with GStreamer’s &lt;a href=&quot;https://gstreamer.freedesktop.org/documentation/rtsp/rtspsrc.html?gi-language=c&quot;&gt;&lt;code&gt;rtspsrc&lt;/code&gt;&lt;/a&gt; plugin:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;gst-launch-1.0 rtspsrc &lt;span class=&quot;token assign-left variable&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;rtsp://127.0.0.1:8554/video &lt;span class=&quot;token assign-left variable&quot;&gt;latency&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; rtph264depay &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; avdec_h264 &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; autovideosink
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or, connect with VLC by entering the RTSP URL into the “Open Network Stream” window.&lt;/p&gt;
&lt;p&gt;If you’re building out a system with multiple video streams, you’ll want to use the GStreamer bindings to define your own RTSP server’s request handling logic. &lt;a href=&quot;https://github.com/tamaggo/gstreamer-examples/blob/master/test_gst_rtsp_server.py&quot;&gt;Here’s an example&lt;/a&gt; of how to use the Python bindings, but bindings for C++ and other languages also exist. NU ROVER has our own in-house implementation which we may decide to open-source once it stabilizes, assuming we have time to do so.&lt;/p&gt;
&lt;h2 id=&quot;extra-signaling-logic&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#extra-signaling-logic&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Extra Signaling Logic&lt;/h2&gt;
&lt;p&gt;One limitation of RTSP is that by default there is no way for a client to list the streams available on the server. If you don’t want to maintain a list of streams on both sides of the connection, you may want to add some extra logic to handle this.&lt;/p&gt;
&lt;p&gt;One approach would be to try to send this data over the RTSP connection somehow, say, by returning it in response to a &lt;code&gt;DESCRIBE&lt;/code&gt; request for a certain URL. Doing this would require the ability to embed this handling logic into whatever RTSP client and server you use, which could be difficult.&lt;/p&gt;
&lt;p&gt;The approach NU ROVER took instead was to use another communication channel which we already had (&lt;a href=&quot;https://www.ros.org/&quot;&gt;ROS&lt;/a&gt; messages) to send this data. However, you could rely on an HTTP API, a database, or some other method. You might want such a protocol to handle:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Listing available stream paths on the server&lt;/li&gt;
&lt;li&gt;Sending metadata for browsing (stream name, description, etc)&lt;/li&gt;
&lt;li&gt;Sending technical metadata (resolution, framerate, camera orientation, bandwidth requirements, etc)&lt;/li&gt;
&lt;li&gt;Handling “exclusivity” of streams (e.g. if two streams are from the same camera but at different resolutions, and only one can be used at a time)&lt;/li&gt;
&lt;li&gt;Sending calibration data and exact positions for relevant cameras, to enable computer vision uses on the other end of the RTSP stream&lt;/li&gt;
&lt;li&gt;Enabling or disabling baked-in overlays in the video signals (timestamp, camera name, etc)&lt;/li&gt;
&lt;li&gt;Pausing the video stream to take high-resolution “snapshots” of the video feed&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;clients&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#clients&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Clients&lt;/h1&gt;
&lt;p&gt;What client are you going to use to decode and display the video signal? You’ll likely want your video feeds to be integrated into whatever control UI you use for other operations, so this will depend on your situation.&lt;/p&gt;
&lt;h2 id=&quot;vlc&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#vlc&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;VLC&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.videolan.org/vlc/&quot;&gt;VLC Media Player&lt;/a&gt; is a common program for playing various media files, and it can play video from an RTSP source. Go to “File” → “Open Network Stream” and enter your RTSP URL.&lt;/p&gt;
&lt;p&gt;VLC is typically meant for watching movies or other prerecorded content, meaning it buffers video to smooth out playback, dramatically increasing latency. While there are ways to reduce the latency (I couldn’t find a good comprehensive source, so just search for “VLC RTSP low latency”), I haven’t had much success. I’d suggest keeping VLC around to help with troubleshooting streams, but not to use it in your “production” system.&lt;/p&gt;
&lt;h2 id=&quot;gstreamer&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#gstreamer&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;GStreamer&lt;/h2&gt;
&lt;p&gt;GStreamer’s &lt;code&gt;rtspsrc&lt;/code&gt; can also load video data over RTSP, and its &lt;code&gt;glimagesink&lt;/code&gt; plugin can display video data in a window, making it functional as a standalone RTSP viewer. (Remember to specify &lt;code&gt;latency=0&lt;/code&gt; for &lt;code&gt;rtspsrc&lt;/code&gt;.) However, if you want your video feed to be displayed in the same window as the rest of your UI, you’ll likely need something more complex.&lt;/p&gt;
&lt;h2 id=&quot;qt-etc&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#qt-etc&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Qt, etc.&lt;/h2&gt;
&lt;p&gt;Bindings often exist to display media from a GStreamer pipeline in applications built with frameworks like Qt (&lt;a href=&quot;https://gstreamer.freedesktop.org/modules/qt-gstreamer.html&quot;&gt;QtGStreamer&lt;/a&gt;), React Native (&lt;a href=&quot;https://github.com/Kalyzee/react-native-gstreamer&quot;&gt;&lt;code&gt;react-native-gstreamer&lt;/code&gt;&lt;/a&gt;), and others. You can find a pipeline that works by launching with the &lt;code&gt;gst-launch-1.0&lt;/code&gt; command, then copy it into your application.&lt;/p&gt;
&lt;h2 id=&quot;browsers&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#browsers&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Browsers&lt;/h2&gt;
&lt;p&gt;Web-based user interfaces are becoming much more popular, and NU ROVER has our own based on React. Of course, you can’t run arbitrary C++ code inside of a browser sandbox, nor can you access UDP/TCP ports directly, so embedding a GStreamer widget directly in the browser isn’t feasible.&lt;/p&gt;
&lt;h1 id=&quot;browser-specifics&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#browser-specifics&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Browser Specifics&lt;/h1&gt;
&lt;p&gt;Sending low-latency video to a web browser is more difficult than it would seem. Browsers are built for media consumption, meaning they often buffer video to smooth out playback at the cost of higher latency. There are quite a few ways to include videos in a web browser, none optimal.&lt;/p&gt;
&lt;h2 id=&quot;http-live-streaming-1&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#http-live-streaming-1&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;HTTP Live Streaming&lt;/h2&gt;
&lt;p&gt;HLS seems like it’s exactly right for this use case, but the required “chunking” of video data adds a high amount of latency, making it unusable for a low-latency video feed.&lt;/p&gt;
&lt;h2 id=&quot;raw-h264&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#raw-h264&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Raw H.264&lt;/h2&gt;
&lt;p&gt;It is possible to directly send an H.264 stream to the browser. However, pre-existing solutions for &lt;em&gt;streaming&lt;/em&gt; H.264 are difficult to come by. One example is the ROS &lt;a href=&quot;https://github.com/RobotWebTools/web_video_server&quot;&gt;&lt;code&gt;web_video_server&lt;/code&gt;&lt;/a&gt; package, which implements its own HTTP server to handle this.&lt;/p&gt;
&lt;p&gt;The benefit of this approach is that video can be embedded in a website with a &lt;code&gt;&amp;#x3C;video&gt;&lt;/code&gt; tag, and the video is compressed when entering the browser, reducing bandwidth use and allowing the browser to choose the most efficient decoder for the platform. The important drawback is that &lt;em&gt;there is no way to control the amount of buffering that the browser does to the video feed&lt;/em&gt;, and browsers like to buffer a significant chunk of frames to ensure smooth playback. NU ROVER experimented with using JavaScript to automatically seek the video to the most recent frame in the buffer, but found this difficult and unreliable.&lt;/p&gt;
&lt;h2 id=&quot;motion-jpeg-1&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motion-jpeg-1&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motion JPEG&lt;/h2&gt;
&lt;p&gt;One potential approach is to run a GStreamer pipeline on the client which decodes the H.264 data, then compresses each frame as a JPEG, then sends those into the browser.&lt;/p&gt;
&lt;p&gt;Sending a series of images to the browser can be done using the &lt;a href=&quot;https://en.wikipedia.org/wiki/Motion_JPEG#Video_streaming&quot;&gt;&lt;code&gt;multipart/x-mixed-replace&lt;/code&gt; mimetype&lt;/a&gt;: the server will send an HTTP response with &lt;code&gt;Content-Type: multipart/x-mixed-replace;boundary=--&amp;#x3C;boundary_name&gt;&lt;/code&gt;, then send a series of MJPG frames, separated by &lt;code&gt;--&amp;#x3C;boundary_name&gt;&lt;/code&gt; lines.&lt;/p&gt;
&lt;p&gt;This is the approach NU ROVER currently uses. We’ve found that encoding and decoding JPEG images adds a small but acceptable amount of latency. We use an in-house project to serve these images, starting and stopping the underlying GStreamer pipelines and RTSP streams on demand.&lt;/p&gt;
&lt;p&gt;A tip for using GStreamer with an application like this: to send buffers from a GStreamer pipeline into your application, you’ll want to use &lt;code&gt;appsink&lt;/code&gt;. Here’s an &lt;a href=&quot;https://github.com/jackersson/gst-python-tutorials/blob/master/launch_pipeline/run_appsink.py&quot;&gt;example&lt;/a&gt; of code that uses this.&lt;/p&gt;
&lt;h2 id=&quot;motion-bmp&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motion-bmp&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motion BMP?&lt;/h2&gt;
&lt;p&gt;In theory, you could send a &lt;code&gt;multipart/x-mixed-replace&lt;/code&gt; payload with any image format, not just JPEG. Sending an uncompressed bitmap image (&lt;code&gt;.bmp&lt;/code&gt;), for instance, would remove the need to encode and decode the JPEG but would increase the data sent to the browser. In our case, we’re running the client on the same machine as the browser, meaning this added data transfer is largely irrelevant.&lt;/p&gt;
&lt;p&gt;This approach is a bit harder to implement than Motion JPEG: effectively, you just need to add the proper headers to make each frame a BMP file, but we haven’t found a good GStreamer plugin that does this. In theory, this could be done in the HTTP server when the GStreamer buffers are read.&lt;/p&gt;
&lt;h2 id=&quot;stream-limit&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#stream-limit&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Stream Limit&lt;/h2&gt;
&lt;p&gt;If you try to run more than six concurrent Motion JPEG streams into a browser, you’ll notice that beyond the first six, none of them load. To prevent creating too many HTTP connections when a site loads, browsers will restrict the number of concurrent HTTP connections to a single host to six.&lt;/p&gt;
&lt;p&gt;In Firefox, it’s possible to change this setting by going to &lt;code&gt;about:config&lt;/code&gt; and changing the &lt;a href=&quot;https://kb.mozillazine.org/Network.http.max-persistent-connections-per-server&quot;&gt;&lt;code&gt;network.http.max-persistent-connections-per-server&lt;/code&gt;&lt;/a&gt; setting from &lt;code&gt;6&lt;/code&gt; to a higher value, say, &lt;code&gt;20&lt;/code&gt;. However, there is no way to change this setting in Chrome.&lt;/p&gt;
&lt;p&gt;One workaround, if you have a DNS server handy or can modify &lt;code&gt;/etc/hosts&lt;/code&gt;, is to configure multiple subdomains for the same server, as described in this &lt;a href=&quot;https://stackoverflow.com/a/23887902&quot;&gt;StackOverflow answer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;An example &lt;code&gt;/etc/hosts&lt;/code&gt; file could look like this:&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;cameras1.local 192.168.1.10
cameras2.local 192.168.1.10
cameras3.local 192.168.1.10
cameras4.local 192.168.1.10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatively, a DNS server could be used with a &lt;a href=&quot;https://en.wikipedia.org/wiki/Wildcard_DNS_record&quot;&gt;wildcard record&lt;/a&gt; to allow any subdomain to work.&lt;/p&gt;
&lt;p&gt;Then, ensure that no more than six streams are loaded using the same subdomain, and all streams should work fine.&lt;/p&gt;
&lt;h2 id=&quot;webrtc&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#webrtc&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;WebRTC&lt;/h2&gt;
&lt;p&gt;One potential approach is using &lt;a href=&quot;https://web.dev/webrtc-basics/&quot;&gt;WebRTC&lt;/a&gt;, which is built for low-latency applications like video conferencing. We haven’t investigated this approach yet for a few reasons. WebRTC is primarily aimed at browser-based applications exchanging data with each other, not receiving data from a non-browser application, so example code for non-browser environments streaming into browsers is hard to find. WebRTC is also built around the assumption that it needs to traverse NAT using a &lt;a href=&quot;https://en.wikipedia.org/wiki/STUN&quot;&gt;STUN&lt;/a&gt; server — not only is that not necessary for us, it’s impossible as our network runs completely offline. That said, the use of WebRTC for low-latency streaming holds potential. I’d love to see this investigated, and hope I’ll get the time to play around with it myself at some point.&lt;/p&gt;
&lt;h1 id=&quot;conclusion&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;We’ve walked through the steps in building a camera streaming pipeline, from the choice of individual camera modules to the framework used to display them to the user. This guide is the resource I wish our team had when developing our streaming pipeline, and covers many of the pitfalls and decisions that will need to be made. It’s far from complete; there are many techniques and topics in streaming to explore that simply fall outside of what we’ve worked with, but I hope it’s useful in whatever system you’re building nonetheless.&lt;/p&gt;</content:encoded></item><item><title>LDSP: low-level audio programming with rooted Android phones</title><link>https://breq.dev/2023/06/17/ldsp/</link><guid isPermaLink="true">https://breq.dev/2023/06/17/ldsp/</guid><description>Or, &quot;How I Helped Author A Paper By Messing Around With Build Systems&quot;</description><pubDate>Sat, 17 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A few weeks ago, I achieved one of my dreams: being on a team that publishes a paper from doing something cool. Here it is in full (shoutout &lt;a href=&quot;https://www.nime.org/&quot;&gt;NIME&lt;/a&gt; for being open-access):&lt;/p&gt;
&lt;iframe src=&quot;https://drive.google.com/file/d/1UVR7LiwlrKoff6cJI32-H6zWrwyu4-4R/preview&quot; class=&quot;w-full mb-2&quot; height=&quot;500&quot; allow=&quot;autoplay&quot;&gt;&lt;/iframe&gt;
&lt;p class=&quot;caption&quot;&gt;You can see my cameo as a hand model in Figure 5.&lt;/p&gt;
&lt;h1 id=&quot;our-vision&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#our-vision&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Our Vision&lt;/h1&gt;
&lt;p&gt;The project was pitched to me by my advisor roughly like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Boards like the Raspberry Pi, &lt;a href=&quot;https://bela.io/&quot;&gt;Bela&lt;/a&gt;, etc. are excellent tools that enable people to learn audio programming and make creative instruments using sensor data.&lt;/li&gt;
&lt;li&gt;These boards can be expensive, and are especially difficult to find in certain parts of the world like Latin America.&lt;/li&gt;
&lt;li&gt;A commodity smartphone, even an older one, has plenty of sensors, processing power, and audio capabilities to support this use case.&lt;/li&gt;
&lt;li&gt;Old smartphones, specifically Android ones, are quite readily available in most parts of the world.&lt;/li&gt;
&lt;li&gt;We just need to find a way to get a software environment low-latency audio I/O on these devices and package it into something easy to use.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;my-work&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#my-work&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;My Work&lt;/h1&gt;
&lt;p&gt;Our approach was to circumvent the JVM entirely by rooting the phones, building binaries for them on a host computer, and executing these binaries directly. This gave us more direct access to the hardware, at the cost of a more difficult setup process. I focused on two main goals: our sensor I/O and our build system.&lt;/p&gt;
&lt;h2 id=&quot;sensor-io&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#sensor-io&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Sensor I/O&lt;/h2&gt;
&lt;p&gt;This work involved working with &lt;code&gt;libandroid&lt;/code&gt;, Android’s internal library used for handling device functions. I spent a ton of time in Android Code Search to investigate how everything fits together, and started to put together some test functions for adjusting sensor sampling rates and reading data, which my advisor then turned into LDSP’s user-facing sensor API, providing functions like &lt;code&gt;sensorRead&lt;/code&gt; — taking heavy inspiration from the Arduino IDE’s simple functions like &lt;code&gt;analogRead&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;build-system&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#build-system&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Build System&lt;/h2&gt;
&lt;p&gt;Our build system draws heavily on the &lt;a href=&quot;https://developer.android.com/ndk&quot;&gt;Android NDK&lt;/a&gt;, or Native Development Kit. This toolchain is designed to build smaller native components within a larger JVM-based app, so we needed to circumvent the intended way of doing things a bit.&lt;/p&gt;
&lt;p&gt;Initially, we just used manually invoked the &lt;code&gt;g++&lt;/code&gt; binary included with the NDK to compile our executables. However, as the project grew and we began pulling in more and more libraries, it became clear another solution was needed. We decided to start with Makefiles, due to their popularity and simplicity. This worked for a while, and even let us include some other scripts in the file (e.g. we set up &lt;code&gt;make push&lt;/code&gt; to push the binaries to the phone).&lt;/p&gt;
&lt;p&gt;This solution worked well as we continued development, but started to stretch when we wanted to make our build system more user-friendly. We wanted to provide a configuration file for each phone we supported, so users would not need to configure compiler settings for their phone manually. Many phone model names contain spaces. Furthermore, we wanted to let users specify a path to their project, and compile some of their files alongside our files. Allowing user project names to contain spaces was a hard requirement for us.&lt;/p&gt;
&lt;p&gt;As it turns out, Makefiles handle spaces extremely poorly — not only do they have to be escaped differently in different places, but many functions treat a variable with spaces implicitly as a list, causing issues. Many &lt;code&gt;make&lt;/code&gt; builtins simply could not handle spaces in filenames, so we were forced to use &lt;code&gt;$(shell ...)&lt;/code&gt; and rely on shell commands (which aren’t portable across operating systems). Our solution was still plagued with bugs.&lt;/p&gt;
&lt;p&gt;Eventually, we gave up on pure Makefiles and I started to investigate other build systems. Ninja seemed promising, and has the backing of projects like Chromium and LLVM. We rolled out some CMake files across the repo, and set the output format to Ninja files, and… success!&lt;/p&gt;
&lt;p&gt;Of course, CMake isn’t necessarily built for running random commands in the way pure Makefiles are, so I created a few wrapper scripts: &lt;code&gt;ldsp.sh&lt;/code&gt; for Linux and macOS and &lt;code&gt;ldsp.bat&lt;/code&gt; for Windows. Each script allows configuring the project, building (with incremental builds), pushing the build to the phone, and running the build.&lt;/p&gt;
&lt;p&gt;We got to see everything come together in an all-day workshop we did with many students from NU Sound. While the install process was a bit convoluted, most participants were able to get a toolchain up and running on their computer!&lt;/p&gt;
&lt;h1 id=&quot;future-goals&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#future-goals&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Future Goals&lt;/h1&gt;
&lt;p&gt;The biggest hurdle with our current setup is complexity: participants need the Android NDK, CMake, Ninja, ADB (Android Debug Bridge), and more installed on their computer. If we could package everything into an easy-to-install application, we could make this project more accessible to those unfamiliar with the command line or programming in general, and make it much more fitting as an educational tool. Furthermore, if we could install this package onto the phone itself, we could remove the need for a host device altogether, making our work accessible to those without a laptop or desktop.&lt;/p&gt;
&lt;p&gt;I’ve been working on cross-compiling LLVM to run natively on an Android host. Admittedly, I haven’t had much luck so far, but it’s still early days. Once we have this crucial portion working, we can pivot to developing a web UI which runs on the phone to support editing and running code from any connected device, then package everything up into an .apk for people to install.&lt;/p&gt;
&lt;h1 id=&quot;thanks&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#thanks&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Thanks&lt;/h1&gt;
&lt;p&gt;I want to thank my advisor, &lt;a href=&quot;https://toomuchidle.com/&quot;&gt;Victor Zappi&lt;/a&gt;, for giving me the opportunity to work on this amazing project.&lt;/p&gt;</content:encoded></item><item><title>How To Reverse an Android App</title><link>https://breq.dev/2023/06/16/transitapp-reversing/</link><guid isPermaLink="true">https://breq.dev/2023/06/16/transitapp-reversing/</guid><description>I really wanted to unlock the 🎈 emoji without spending $25</description><pubDate>Fri, 16 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;This blog post follows my journey to learn about reverse-engineering on Android over a few months. Unlike a traditional project writeup, the structure of this piece matches the process of discovery I took. Dead-ends, useless tangents, and inefficient solutions have been intentionally left in.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;the-idea&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-idea&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Idea&lt;/h1&gt;
&lt;p&gt;I live in Boston currently, which has a wonderfully extensive &lt;a href=&quot;mbta&quot;&gt;transit network&lt;/a&gt;. To navigate between unfamiliar areas, I use an app called &lt;a href=&quot;transitapp&quot;&gt;Transit&lt;/a&gt;. Transit (or TransitApp, as I tend to call it) tracks bus, train, and subway departures in realtime to provide directions and time predictions.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;A screenshot of the app&amp;#39;s interface, showing a map and a list of transit lines.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1080&quot; height=&quot;2400&quot; src=&quot;https://breq.dev/_astro/screenshot.udW6dkO6_1SCxuH.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;TransitApp also has a “gamification” system, in which you can set an avatar, report the location of trains from your phone, and show up on leaderboards.&lt;/p&gt;
&lt;p&gt;Instead of public-facing usernames or profile pictures, TransitApp’s social features work off of random emoji and generated names. You are given the option to generate a new emoji/name combination.&lt;/p&gt;
&lt;p&gt;For reasons I won’t get into, I really wanted the 🎈 emoji as my profile. It’s not on the list of emoji that the shuffling goes through (I shuffled for quite a while to confirm). If you pay for “Transit Royale” (the paid tier), you can choose your emoji yourself. However, I was bored, so I decided to see if I could find a more fun way to get it.&lt;/p&gt;
&lt;p&gt;My hypothesis was that the app is doing this “shuffling” logic on the client side, then sending a POST request to the server with the emoji to be chosen. If true, this would mean that I could replay that request, but with an emoji of my choosing.&lt;/p&gt;
&lt;h1 id=&quot;methods&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#methods&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Methods&lt;/h1&gt;
&lt;h2 id=&quot;android-emulator&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#android-emulator&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Android Emulator&lt;/h2&gt;
&lt;p&gt;Because messing with a physical Android device seemed tricky, I decided to try using the Android emulator built into Android Studio. (No need to create a project: just click the three dots in the upper right and choose “Virtual Device Manager.”) After picking a configuration that supported the Google Play Store (I chose the Pixel 4), I installed TransitApp and logged in without issue.&lt;/p&gt;
&lt;h2 id=&quot;burp-suite&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#burp-suite&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Burp Suite&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;burpsuite&quot;&gt;Burp Suite&lt;/a&gt; is the standard tool for inspecting and replaying HTTP network traffic. Burp Suite creates an HTTP proxy, and is able to inspect traffic via this proxy.&lt;/p&gt;
&lt;p&gt;I began by setting Burp to bind on all interfaces, then set the proxy in the emulated phone’s settings to point at my computer’s IP address and proxy port.&lt;/p&gt;
&lt;p&gt;Burp Suite’s analysis tools effectively break HTTPS, since it’s the literal definition of a man-in-the-middle attack. In other words, TLS protects the connection from my phone to any cloud services, and for Burp to mess with that, it needs a way to circumvent TLS. One approach is to install Burp’s certificates as a “trusted” certificate, effectively making Burp Suite able to impersonate any website it wants.&lt;/p&gt;
&lt;p&gt;Most guides for manually installing CA certificates on Android require a rooted operating system, but with a reasonably recent OS, it’s possible to install them in the phone’s settings, by going to: &lt;code&gt;Settings&lt;/code&gt; -&gt; &lt;code&gt;Security &amp;#x26; privacy&lt;/code&gt; -&gt; &lt;code&gt;More settings&lt;/code&gt; -&gt; &lt;code&gt;Encryption &amp;#x26; credentials&lt;/code&gt; -&gt; &lt;code&gt;Install a certificate&lt;/code&gt; -&gt; &lt;code&gt;CA certificate&lt;/code&gt;. This adds a “Network may be monitored” warning to the phone’s Quick Settings page.&lt;/p&gt;
&lt;p&gt;This works perfectly! Now let’s just fire up TransitApp, and… nothing. It looks like TransitApp doesn’t respect the user’s proxy settings.&lt;/p&gt;
&lt;p&gt;A few apps claim to be able to use a VPN profile to force all traffic over a proxy, but this seems to not work properly for any I tested.&lt;/p&gt;
&lt;h2 id=&quot;wireshark&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#wireshark&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Wireshark&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;wireshark&quot;&gt;Wireshark&lt;/a&gt; is a much more general-purpose network traffic capture tool. It supports capturing network traffic and filtering, making it relatively easy to inspect traffic.&lt;/p&gt;
&lt;p&gt;However, Wireshark cannot circumvent TLS. As a result, even though the presence of traffic is visible, it cannot be actually inspected. When filtering for DNS traffic, though, we do get something helpful: the domain names that TransitApp uses. We see a few:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;api-cdn.transitapp.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stats.transitapp.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;service-alerts.transitapp.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;api.revenuecat.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bgtfs.transitapp.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;api.transitapp.com&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the exception of RevenueCat, which seems to manage in-app subscriptions, all domains are &lt;code&gt;*.transitapp.com&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;burp-suite-invisible-mode&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#burp-suite-invisible-mode&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Burp Suite Invisible Mode&lt;/h2&gt;
&lt;p&gt;Burp Suite’s &lt;a href=&quot;https://portswigger.net/burp/documentation/desktop/tools/proxy/invisible&quot;&gt;Invisible Mode&lt;/a&gt; allows the proxy to work for devices that aren’t aware of its existence, by relying on the host OS to override DNS queries for the relevant domain names and send them to the proxy instead. To get this to work, we can run the Burp &lt;code&gt;.jar&lt;/code&gt; with &lt;code&gt;sudo&lt;/code&gt;, then set up two proxies: one on port 80 for HTTP and one on port 443 for HTTPS. Make sure to enable “invisible proxying” for both.&lt;/p&gt;
&lt;p&gt;Since we’re using invisible proxying, we’ll need to explicitly tell Burp where to forward the traffic — the HTTP(S) requests themselves won’t have enough information for Burp to route them onwards. We can make a DNS request to find the IP address of the serer hosting &lt;code&gt;api.transitapp.com&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;$ dig @1.1.1.1 api.transitapp.com

; &amp;#x3C;&amp;#x3C;&gt;&gt; DiG 9.10.6 &amp;#x3C;&amp;#x3C;&gt;&gt; @1.1.1.1 api.transitapp.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; -&gt;&gt;HEADER&amp;#x3C;&amp;#x3C;- opcode: QUERY, status: NOERROR, id: 38753
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;api.transitapp.com.		IN	A

;; ANSWER SECTION:
api.transitapp.com.	248	IN	A	34.102.188.182

;; Query time: 55 msec
;; SERVER: 1.1.1.1#53(1.1.1.1)
;; WHEN: Fri Jun 16 18:27:14 EDT 2023
;; MSG SIZE  rcvd: 63
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Configure both proxies to route traffic to that IP address, keeping the ports the same.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;A screenshot of Burp Suite&amp;#39;s proxy settings showing the stated configuration&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2876&quot; height=&quot;1724&quot; src=&quot;https://breq.dev/_astro/burp.F_YNXbcV_Z1B7gvx.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you’ve played with networking before, your first instinct will probably be to mess with the &lt;code&gt;/etc/hosts&lt;/code&gt; file to override DNS for the domains you want to intercept. This is a pretty common technique for web-based attacks, so let’s give it a try. One hiccup: we can’t put wildcards directly in &lt;code&gt;/etc/hosts&lt;/code&gt;, so we’ll have to list each one individually. Worse, we’ll actually have to only do one at a time, since Burp Suite can only forward traffic to one IP at a time. Start with &lt;code&gt;api.transitapp.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Aaaand… it still doesn’t work. Android is using something called Private DNS to automatically route traffic over HTTPS, meaning it can’t be tampered with as easily as traditional DNS. But even if you turn that off in the emulated phone’s network settings, it still doesn’t work, because the emulator doesn’t respect the &lt;code&gt;/etc/hosts&lt;/code&gt; file. You’ll need to run a DNS server.&lt;/p&gt;
&lt;p&gt;First, find the IP address of the emulated phone and your computer. In the phone, go to the network settings page, then look for “IP Address” and “Gateway”: those are the IPs of the phone and your computer, respectively.&lt;/p&gt;
&lt;p&gt;Install dnsmasq on your host computer and run it (here’s a nice &lt;a href=&quot;https://gist.github.com/ogrrd/5831371&quot;&gt;guide&lt;/a&gt; for macOS). Set the Android DNS settings to point to your computer’s IP. And then update your &lt;code&gt;/etc/hosts&lt;/code&gt; entry to point to the IP address of your computer on the network instead of &lt;code&gt;127.0.0.1&lt;/code&gt;, since otherwise the emulated phone would attempt to connect to &lt;em&gt;itself&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Even still, this doesn’t work. If you try to inspect traffic, relevant features of the app will simply stop working and you will not see any connection trying to be made.&lt;/p&gt;
&lt;h2 id=&quot;static-analysis&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#static-analysis&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Static Analysis&lt;/h2&gt;
&lt;p&gt;Maybe some static analysis could help? We can download an APK from a &lt;a href=&quot;https://m.apkpure.com/transit-bus-subway-times/com.thetransitapp.droid/download&quot;&gt;definitely legitimate source&lt;/a&gt;, then unzip it (&lt;code&gt;.apk&lt;/code&gt; files are just &lt;code&gt;.zip&lt;/code&gt; files).&lt;/p&gt;
&lt;p&gt;The first thing we can do is to start looking for URLs. We already know that some URLs start with &lt;code&gt;api.transitapp.com&lt;/code&gt;, so let’s try looking for that:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ rg &lt;span class=&quot;token string&quot;&gt;&quot;api.transitapp.com&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No results. What about just &lt;code&gt;&quot;transitapp.com&quot;&lt;/code&gt;? Still nothing.&lt;/p&gt;
&lt;p&gt;It could be substituted in somewhere. Maybe we could look for the beginning &lt;code&gt;&quot;http&quot;&lt;/code&gt; or &lt;code&gt;&quot;https&quot;&lt;/code&gt; part of a URL?&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ rg http
assets/cacert.pem
&lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;:&lt;span class=&quot;token comment&quot;&gt;## https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt&lt;/span&gt;

okhttp3/internal/publicsuffix/NOTICE
&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;:https://publicsuffix.org/list/public_suffix_list.dat
&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;:https://mozilla.org/MPL/2.0/

META-INF/services/io.grpc.ManagedChannelProvider
&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;:io.grpc.okhttp.d

google/protobuf/source_context.proto
&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;:// https://developers.google.com/protocol-buffers/

google/protobuf/empty.proto
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;many similar protobuf results omitted&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;


META-INF/MANIFEST.MF
&lt;span class=&quot;token number&quot;&gt;494&lt;/span&gt;:Name: okhttp3/internal/publicsuffix/NOTICE
&lt;span class=&quot;token number&quot;&gt;497&lt;/span&gt;:Name: okhttp3/internal/publicsuffix/publicsuffixes.gz

META-INF/CERT.SF
&lt;span class=&quot;token number&quot;&gt;495&lt;/span&gt;:Name: okhttp3/internal/publicsuffix/NOTICE
&lt;span class=&quot;token number&quot;&gt;498&lt;/span&gt;:Name: okhttp3/internal/publicsuffix/publicsuffixes.gz

res/56.xml
&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;:&lt;span class=&quot;token operator&quot;&gt;&amp;#x3C;&lt;/span&gt;resources xmlns:tools&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http://schemas.android.com/tools&quot;&lt;/span&gt;

res/sd.xml
&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;:&lt;span class=&quot;token operator&quot;&gt;&amp;#x3C;&lt;/span&gt;resources xmlns:tools&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http://schemas.android.com/tools&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Okay, that’s interesting. “okhttp3”? This seems like it could be related to how the application makes HTTP requests to the API. But this still leaves us with a few questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Why weren’t we able to find the URLs?&lt;/strong&gt; From a bit of research, it looks like Android apps store Java code in &lt;code&gt;.dex&lt;/code&gt; files: the Dalvik Executable Format. (Dalvik is the name of the virtual machine used to run Android apps.) It is possible that this format uses compression or other techniques which would prevent a literal string from appearing. Running &lt;code&gt;rg&lt;/code&gt; with the &lt;code&gt;-a&lt;/code&gt; parameter does show some matches in a binary file, but they seem to be in a section of the file which just stores string literals, and based on the limited number of matches, it is likely that URLs are assembled at runtime (meaning we won’t find a fully-formed URL in the source).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Why did the app refuse to connect via our monitoring setup?&lt;/strong&gt; Here’s where we need to dig in to how OkHttp works a bit more.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;certificate-pinning&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#certificate-pinning&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Certificate Pinning&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://square.github.io/okhttp/&quot;&gt;OkHttp&lt;/a&gt; is a library made by Square for making HTTP requests on Android (or other Java platforms). The fact that it was developed by Square, a payment processing company, is a clue that they might be taking steps to secure the connection that most apps wouldn’t.&lt;/p&gt;
&lt;p&gt;Here’s a snippet from the front page of the OkHttp documentation, emphasis mine:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;OkHttp perseveres when the network is troublesome: it will silently recover from common connection problems. If your service has multiple IP addresses, OkHttp will attempt alternate addresses if the first connect fails. This is necessary for IPv4+IPv6 and services hosted in redundant data centers. OkHttp supports modern TLS features (TLS 1.3, ALPN, &lt;strong&gt;certificate pinning&lt;/strong&gt;). It can be configured to fall back for broad connectivity.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Certificate pinning sounds relevant to what we’re doing here, considering our approach is to supply an alternate certificate. So what is it? According to the &lt;a href=&quot;https://square.github.io/okhttp/4.x/okhttp/okhttp3/-certificate-pinner/&quot;&gt;OkHttp docs&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Constrains which certificates are trusted. Pinning certificates defends against attacks on certificate authorities. &lt;strong&gt;It also prevents connections through man-in-the-middle certificate authorities either known or unknown to the application’s user.&lt;/strong&gt; This class currently pins a certificate’s Subject Public Key Info as described on &lt;a href=&quot;https://www.imperialviolet.org/2011/05/04/pinning.html&quot;&gt;Adam Langley’s Weblog&lt;/a&gt;. Pins are either base64 SHA-256 hashes as in HTTP Public Key Pinning (HPKP) or SHA-1 base64 hashes as in Chromium’s static certificates.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Our monitoring setup is such a man-in-the-middle scenario: we use our own certificate authority (in this case, Burp Suite) to “re-secure” the actual connection from TransitApp, after we’ve monitored and tampered with the connection.&lt;/p&gt;
&lt;p&gt;So, how do we get around certificate pinning? We’ll need to get quite a bit more serious about our decompilation efforts, since we’ll need to remove the hash of the existing certificate in the code and replace that with the hash of our own certificate. I roughly followed &lt;a href=&quot;https://fullstackhero.medium.com/bypass-okhttp-certificatepinner-on-android-a085b8074e25&quot;&gt;this guide&lt;/a&gt; for this step.&lt;/p&gt;
&lt;h2 id=&quot;apk-analysis&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#apk-analysis&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;APK Analysis&lt;/h2&gt;
&lt;p&gt;Android Studio helpfully includes an &lt;a href=&quot;https://developer.android.com/studio/debug/apk-analyzer&quot;&gt;APK Analyzer&lt;/a&gt; tool which we can use to peek a bit deeper into the app. Create a new project,then drag and drop the TransitApp APK into the main window.&lt;/p&gt;
&lt;p&gt;Unfortunately, method names have pretty much all been minified, so you’ll see a bunch of &lt;code&gt;u4&lt;/code&gt;, &lt;code&gt;a5&lt;/code&gt;, &lt;code&gt;o4&lt;/code&gt;, etc. Android Studio also doesn’t let us view or modify the Java code within each method. However, note that even the OkHttp3 code seems to be minified, and I couldn’t find any reference to CertificatePinner (although &lt;code&gt;rg -a CertificatePinner&lt;/code&gt; returned a few matches, so maybe there’s hope?)&lt;/p&gt;
&lt;p&gt;An open-source tool called &lt;a href=&quot;https://ibotpeaches.github.io/Apktool/&quot;&gt;APKtool&lt;/a&gt; might save us. APKtool allows us to essentially decompile an APK into Smali (essentially an assembly listing for Java bytecode), make modifications, then recompile it. To start, let’s see if we can find the certificate hash.&lt;/p&gt;
&lt;p&gt;First, download the &lt;code&gt;.jar&lt;/code&gt; from &lt;a href=&quot;https://bitbucket.org/iBotPeaches/apktool/downloads/&quot;&gt;here&lt;/a&gt;, then run it with:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;java&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-jar&lt;/span&gt; apktool_2.7.0.jar d Transit&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt; Bus&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt; Subway&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt; Times_5.13.5_Apkpure.apk
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, dive into the directory named after that APK. We’re looking for the code that invokes &lt;code&gt;CertificatePinner&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;reading-smali-bytecode-working-up&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#reading-smali-bytecode-working-up&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Reading Smali Bytecode: Working Up&lt;/h2&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;rg CertificatePinner
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This seems to give quite a few results within the &lt;code&gt;okhttp3&lt;/code&gt; library: the implementation of certificate pinning, special handling in the connection class, and a few other uses. However, there is one result outside that library:&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;smali_classes2/com/masabi/justride/sdk/platform/AndroidPlatformModule2.smali
91:.method private getCertificatePinner()Lokhttp3/CertificatePinner;
107:    new-instance v1, Lokhttp3/CertificatePinner$a;
111:    invoke-direct {v1}, Lokhttp3/CertificatePinner$a;-&gt;&amp;#x3C;init&gt;()V
206:    invoke-virtual {v1, v3, v4}, Lokhttp3/CertificatePinner$a;-&gt;a(Ljava/lang/String;[Ljava/lang/String;)Lokhttp3/CertificatePinner$a;
215:    invoke-virtual {v1}, Lokhttp3/CertificatePinner$a;-&gt;b()Lokhttp3/CertificatePinner;
250:    invoke-direct {p0}, Lcom/masabi/justride/sdk/platform/AndroidPlatformModule2;-&gt;getCertificatePinner()Lokhttp3/CertificatePinner;
258:    invoke-virtual {p1, v0}, Lokhttp3/OkHttpClient$a;-&gt;d(Lokhttp3/CertificatePinner;)Lokhttp3/OkHttpClient$a;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result on line &lt;code&gt;111&lt;/code&gt; looks perhaps the most interesting to us: invoking the constructor on the &lt;code&gt;CertificatePinner&lt;/code&gt; class. However, it doesn’t seem to be passing in any sort of hash. Let’s consult the OkHttp &lt;a href=&quot;https://square.github.io/okhttp/4.x/okhttp/okhttp3/-certificate-pinner/&quot;&gt;documentation&lt;/a&gt; as to how &lt;code&gt;CertificatePinner&lt;/code&gt;s are constructed:&lt;/p&gt;
&lt;pre class=&quot;language-java&quot; data-language=&quot;java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; hostname &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;publicobject.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;CertificatePinner&lt;/span&gt; certificatePinner &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CertificatePinner&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Builder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hostname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;OkHttpClient&lt;/span&gt; client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OkHttpClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Builder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;certificatePinner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;certificatePinner&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;Request&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Builder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; hostname&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newCall&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Huh, okay, so we’re really looking for a &lt;code&gt;CertificatePinner.Builder&lt;/code&gt;. But &lt;code&gt;rg CertificatePinner.Builder&lt;/code&gt; gives no results.&lt;/p&gt;
&lt;p&gt;Here’s where a Java quirk comes into play: Nested classes like &lt;code&gt;CertificatePinner.Builder&lt;/code&gt; are represented internally using a dollar sign in place of the dot. So we’re really looking for &lt;code&gt;CertificatePinner$Builder&lt;/code&gt;. Make sure to escape it properly:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;rg &lt;span class=&quot;token string&quot;&gt;&apos;CertificatePinner\$Builder&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And… still no results. But don’t lose hope yet—remember our experiments with APK Analyzer? Perhaps the name &lt;code&gt;Builder&lt;/code&gt; got minified. Let’s try to find any nested classes within &lt;code&gt;CertificatePinner&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;rg &lt;span class=&quot;token string&quot;&gt;&apos;CertificatePinner\$&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It looks like there’s a &lt;code&gt;CertificatePinner$a&lt;/code&gt;, &lt;code&gt;CertificatePinner$b&lt;/code&gt;, and &lt;code&gt;CertificatePinner$c&lt;/code&gt;. The &lt;a href=&quot;https://square.github.io/okhttp/4.x/okhttp/okhttp3/-certificate-pinner/&quot;&gt;docs&lt;/a&gt; seem to show three nested classes: &lt;code&gt;Builder&lt;/code&gt;, &lt;code&gt;Companion&lt;/code&gt;, and &lt;code&gt;Pin&lt;/code&gt;. Out of the three, it seems like &lt;code&gt;Builder&lt;/code&gt; is the only one that should need to be used externally. Looking at the one result outside of the &lt;code&gt;okhttp3&lt;/code&gt; package:&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;smali_classes2/com/masabi/justride/sdk/platform/AndroidPlatformModule2.smali
107:    new-instance v1, Lokhttp3/CertificatePinner$a;
111:    invoke-direct {v1}, Lokhttp3/CertificatePinner$a;-&gt;&amp;#x3C;init&gt;()V
206:    invoke-virtual {v1, v3, v4}, Lokhttp3/CertificatePinner$a;-&gt;a(Ljava/lang/String;[Ljava/lang/String;)Lokhttp3/CertificatePinner$a;
215:    invoke-virtual {v1}, Lokhttp3/CertificatePinner$a;-&gt;b()Lokhttp3/CertificatePinner;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’re looking for the call to &lt;code&gt;Builder.add()&lt;/code&gt;, since that will pass in the pin as a hash. Again, the name &lt;code&gt;.add()&lt;/code&gt; will be minified, so we’ll need to be clever. Now that we’ve narrowed things down to a single file, we can start to read through and look for anything interesting. This method stands out (&lt;code&gt;.line&lt;/code&gt; directives omitted for brevity):&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;
&lt;span class=&quot;token keyword&quot;&gt;.method&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getCertificatePinner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;okhttp3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CertificatePinner&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.locals&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;

    iget-object &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;masabi&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;justride&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;sdk&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;platform&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AndroidPlatformModule2&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token field variable&quot;&gt;sdkConfiguration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;masabi&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;justride&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;sdk&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;models&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SdkConfiguration&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;masabi&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;justride&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;sdk&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;models&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SdkConfiguration&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCertificatePins&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    move-result-object &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;

    new-instance &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;okhttp3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CertificatePinner$a&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    invoke-direct &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;okhttp3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CertificatePinner$a&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;&amp;#x3C;init&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;

    invoke-interface &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;iterator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Iterator&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    move-result-object &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;goto_0&lt;/span&gt;
    invoke-interface &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Iterator&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasNext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Z&lt;/span&gt;

    move-result &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;

    if-eqz &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;cond_0&lt;/span&gt;

    invoke-interface &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Iterator&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    move-result-object &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;

    check-cast &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    iget-object &lt;span class=&quot;token register variable&quot;&gt;v3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;masabi&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;justride&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;sdk&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;platform&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AndroidPlatformModule2&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token field variable&quot;&gt;sdkConfiguration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;masabi&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;justride&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;sdk&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;models&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SdkConfiguration&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;masabi&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;justride&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;sdk&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;models&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SdkConfiguration&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getHostname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    move-result-object &lt;span class=&quot;token register variable&quot;&gt;v3&lt;/span&gt;

    const/4 &lt;span class=&quot;token register variable&quot;&gt;v4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x1&lt;/span&gt;

    new-array &lt;span class=&quot;token register variable&quot;&gt;v4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    const/4 &lt;span class=&quot;token register variable&quot;&gt;v5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x0&lt;/span&gt;

    new-instance &lt;span class=&quot;token register variable&quot;&gt;v6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    invoke-direct &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;&amp;#x3C;init&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;

    const-string &lt;span class=&quot;token register variable&quot;&gt;v7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sha256/&quot;&lt;/span&gt;

    invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;StringBuilder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    move-result-object &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;

    aput-object &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v5&lt;/span&gt;

    invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;okhttp3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CertificatePinner$a&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;okhttp3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CertificatePinner$a&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    goto &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;goto_0&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;cond_0&lt;/span&gt;
    invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;okhttp3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CertificatePinner$a&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;okhttp3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CertificatePinner&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    move-result-object &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;

    return-object &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;.end&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;method&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s step through what this is doing, using our intuition to bridge the gaps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Calling &lt;code&gt;SdkConfiguration.getCertificatePins()&lt;/code&gt;, which returns a list of some type (maybe Strings?)&lt;/li&gt;
&lt;li&gt;Creating a &lt;code&gt;CertificatePinner.Builder&lt;/code&gt; (shown here as a &lt;code&gt;CertificatePinner$a&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Iterating through the list of certificate pins&lt;/li&gt;
&lt;li&gt;For each pin, using a &lt;code&gt;StringBuilder&lt;/code&gt; to assemble a hash string (starting with &lt;code&gt;sha256/&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Calling &lt;code&gt;.add()&lt;/code&gt; on the &lt;code&gt;CertificatePinner.Builder&lt;/code&gt; object with the constructed string for each pin&lt;/li&gt;
&lt;li&gt;Returning the result of calling &lt;code&gt;.build()&lt;/code&gt; on the &lt;code&gt;Builder&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This means we’ll need to search a little bit deeper to find the hashes we seek, starting with &lt;code&gt;getCertificatePins()&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ rg getCertificatePins
smali_classes2/com/masabi/justride/sdk/converters/config/SdkConfigurationConverter.smali
&lt;span class=&quot;token number&quot;&gt;454&lt;/span&gt;:    invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;p1&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;, Lcom/masabi/justride/sdk/internal/models/config/SdkConfiguration&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;-&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;getCertificatePins&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;Ljava/util/List&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

smali_classes2/com/masabi/justride/sdk/internal/models/config/SdkConfiguration.smali
&lt;span class=&quot;token number&quot;&gt;762&lt;/span&gt;:.method public getCertificatePins&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;Ljava/util/List&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

smali_classes2/com/masabi/justride/sdk/platform/AndroidPlatformModule2.smali
&lt;span class=&quot;token number&quot;&gt;99&lt;/span&gt;:    invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;v0&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;, Lcom/masabi/justride/sdk/internal/models/config/SdkConfiguration&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;-&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;getCertificatePins&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;Ljava/util/List&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result with &lt;code&gt;.method public&lt;/code&gt; is the definition of the &lt;code&gt;getCertificatePins()&lt;/code&gt; method — let’s start there.&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;.method&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getCertificatePins&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.locals&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.annotation&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;system&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;dalvik&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;annotation&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Signature&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;()&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/util/List&amp;#x3C;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;&gt;;&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.end&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;annotation&lt;/span&gt;

    iget-object &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;masabi&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;justride&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;sdk&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;models&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SdkConfiguration&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token field variable&quot;&gt;certificatePins&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    return-object &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;.end&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;method&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Okay, so we’re just returning the &lt;code&gt;certificatePins&lt;/code&gt; field. It’s just a classic “getter method.” We can track down the field definition:&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;.field&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token field variable&quot;&gt;certificatePins&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.annotation&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;system&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;dalvik&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;annotation&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Signature&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/util/List&amp;#x3C;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;&gt;;&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.end&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;annotation&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;.end&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;field&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Okay, so it’s a &lt;code&gt;private final List&amp;#x3C;String&gt;&lt;/code&gt;. That’s pretty standard. This essentially gives us two options: either these options are set in the constructor, or they’re added later through another public method. Let’s check the constructor first.&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;.method&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;constructor&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;&amp;#x3C;init&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Z&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Z&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.locals&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.annotation&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;system&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;dalvik&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;annotation&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Signature&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;(&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/util/List&amp;#x3C;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;&gt;;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/util/List&amp;#x3C;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;&gt;;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/util/List&amp;#x3C;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;&gt;;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Z&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Z)V&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.end&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;annotation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oh god, that’s 32 lines and we haven’t even gotten to an implementation yet. Here’s the part of the implementation that deals with the certificate pins:&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;move-object &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;p4&lt;/span&gt;

iput-object &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;masabi&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;justride&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;sdk&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;models&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SdkConfiguration&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token field variable&quot;&gt;certificatePins&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So the pins are passed in as a list, in the fifth parameter (&lt;code&gt;p4&lt;/code&gt; is indexed starting at zero.) Let’s keep following this wild goose chase: where is the constructor called?&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ rg &lt;span class=&quot;token string&quot;&gt;&apos;SdkConfiguration;-&gt;&amp;#x3C;init&gt;&apos;&lt;/span&gt;
smali_classes2/com/masabi/justride/sdk/internal/models/config/SdkConfiguration.smali
&lt;span class=&quot;token number&quot;&gt;211&lt;/span&gt;:    invoke-direct/range &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;p0 &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt; p18&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;, Lcom/masabi/justride/sdk/internal/models/config/SdkConfiguration&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;-&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;#x3C;&lt;/span&gt;init&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/util/List&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/util/List&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/util/List&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;ZLjava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Z&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;V

smali_classes2/com/masabi/justride/sdk/internal/models/config/SdkConfiguration&lt;span class=&quot;token variable&quot;&gt;$Builder&lt;/span&gt;.smali
&lt;span class=&quot;token number&quot;&gt;379&lt;/span&gt;:    invoke-direct/range &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;v2 &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt; v21&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;, Lcom/masabi/justride/sdk/internal/models/config/SdkConfiguration&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;-&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;#x3C;&lt;/span&gt;init&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/util/List&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/util/List&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/util/List&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;ZLjava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;Ljava/lang/String&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;ZLcom/masabi/justride/sdk/internal/models/config/SdkConfiguration&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;V
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oh, duh, it’s another builder. At least this one isn’t minified? Let’s take a look at the &lt;code&gt;SdkConfiguration$Builder.smali&lt;/code&gt; file. Alongside brand, country code, and other parameters, we see a few points of interest.&lt;/p&gt;
&lt;p&gt;Here’s the definition of the &lt;code&gt;certificatePins&lt;/code&gt; field on the &lt;em&gt;builder&lt;/em&gt;. Note that it isn’t marked &lt;code&gt;final&lt;/code&gt;, meaning it probably gets assigned to somewhere.&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;.field&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token field variable&quot;&gt;certificatePins&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.annotation&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;system&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;dalvik&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;annotation&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Signature&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/util/List&amp;#x3C;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;&gt;;&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.end&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;annotation&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;.end&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;field&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next in the file is the &lt;code&gt;.build()&lt;/code&gt; method. It does some checks for &lt;code&gt;null&lt;/code&gt;, but other than that, seems pretty boring. Scrolling down a bit, though, we see a definition for &lt;code&gt;setCertificatePins()&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;.method&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setCertificatePins&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;masabi&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;justride&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;sdk&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;models&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SdkConfiguration$Builder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.locals&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.annotation&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;system&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;dalvik&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;annotation&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Signature&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;(&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/util/List&amp;#x3C;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Ljava/lang/String;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;&gt;;)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;Lcom/masabi/justride/sdk/internal/models/config/SdkConfiguration$Builder;&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.end&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;annotation&lt;/span&gt;

    iput-object &lt;span class=&quot;token register variable&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;masabi&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;justride&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;sdk&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;internal&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;models&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SdkConfiguration$Builder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token field variable&quot;&gt;certificatePins&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;List&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    return-object &lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;.end&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;method&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There’s no surprise here regarding what this method does (it just assigns the argument to the &lt;code&gt;certificatePins&lt;/code&gt; field). However, now that we know the name of this method, we can try to look for it elsewhere.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ rg setCertificatePins
smali_classes2/com/masabi/justride/sdk/converters/config/SdkConfigurationConverter.smali
&lt;span class=&quot;token number&quot;&gt;199&lt;/span&gt;:    invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;v2, v3&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;, Lcom/masabi/justride/sdk/internal/models/config/SdkConfiguration&lt;span class=&quot;token variable&quot;&gt;$Builder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;-&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;setCertificatePins&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Ljava/util/List&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;Lcom/masabi/justride/sdk/internal/models/config/SdkConfiguration&lt;span class=&quot;token variable&quot;&gt;$Builder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

smali_classes2/com/masabi/justride/sdk/internal/models/config/SdkConfiguration&lt;span class=&quot;token variable&quot;&gt;$Builder&lt;/span&gt;.smali
&lt;span class=&quot;token number&quot;&gt;770&lt;/span&gt;:.method public setCertificatePins&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Ljava/util/List&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;Lcom/masabi/justride/sdk/internal/models/config/SdkConfiguration&lt;span class=&quot;token variable&quot;&gt;$Builder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It looks like the only place &lt;code&gt;setCertificatePins&lt;/code&gt; is called is within &lt;code&gt;SdkConfigurationConverter&lt;/code&gt;. This is an interesting class, and it’s not immediately clear what it’s doing. A few method names give us a clue, however:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;convertJSONObjectToModel(JSONObject p1)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;convertModelToJSONObject(SdkConfiguration p1)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It would be &lt;em&gt;really&lt;/em&gt; easy if the certificate pins were just stored in a JSON file somewhere… But &lt;code&gt;find . -type f -name &quot;*.json&quot;&lt;/code&gt; comes up empty.&lt;/p&gt;
&lt;p&gt;One quick sanity check: Is &lt;code&gt;SdkConfigurationConverter&lt;/code&gt; even constructed? It could be that we’re looking in the complete wrong part of the code here. Maybe our assumption about certificate pinning isn’t correct after all?&lt;/p&gt;
&lt;p&gt;Note that &lt;code&gt;SdkConfigurationConverter&lt;/code&gt; has a private constructor and a public static &lt;code&gt;create()&lt;/code&gt; method. Therefore, we should be searching for &lt;code&gt;SdkConfigurationConverter.create()&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ rg &lt;span class=&quot;token string&quot;&gt;&apos;SdkConfigurationConverter;-&gt;create&apos;&lt;/span&gt;
smali_classes2/com/masabi/justride/sdk/jobs/config/ProcessConfigurationDataJob.smali
&lt;span class=&quot;token number&quot;&gt;1096&lt;/span&gt;:    invoke-static &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;, Lcom/masabi/justride/sdk/converters/config/SdkConfigurationConverter&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;-&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;create&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;Lcom/masabi/justride/sdk/converters/config/SdkConfigurationConverter&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Okay, so it’s used &lt;em&gt;somewhere&lt;/em&gt; at least. Is &lt;code&gt;ProcessConfigurationDataJob&lt;/code&gt; invoked anywhere? It looks like it has a &lt;code&gt;create()&lt;/code&gt; method, just like &lt;code&gt;SdkConfigurationConverter&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ rg &lt;span class=&quot;token string&quot;&gt;&apos;ProcessConfigurationDataJob;-&gt;create&apos;&lt;/span&gt;
smali_classes2/com/masabi/justride/sdk/AndroidJustRideSdkBuilder.smali
&lt;span class=&quot;token number&quot;&gt;121&lt;/span&gt;:    invoke-static &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;v0&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;, Lcom/masabi/justride/sdk/jobs/config/ProcessConfigurationDataJob&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;-&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;create&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Lcom/masabi/justride/sdk/platform/crypto/PlatformSignatureVerifier&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;Lcom/masabi/justride/sdk/jobs/config/ProcessConfigurationDataJob&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And just to finish going up the chain, where is this instantiated?&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ rg AndroidJustRideSdkBuilder
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

smali_classes2/com/thetransitapp/droid/shared/TransitApp.smali
&lt;span class=&quot;token number&quot;&gt;524&lt;/span&gt;:    invoke-static &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;, Lcom/masabi/justride/sdk/AndroidJustRideSdk&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;-&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;builder&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;Lcom/masabi/justride/sdk/AndroidJustRideSdkBuilder&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;532&lt;/span&gt;:    invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;p1, p0&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;, Lcom/masabi/justride/sdk/AndroidJustRideSdkBuilder&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;-&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Landroid/app/Application&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;Lcom/masabi/justride/sdk/AndroidJustRideSdkBuilder&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;540&lt;/span&gt;:    invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;p1, v0&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;, Lcom/masabi/justride/sdk/AndroidJustRideSdkBuilder&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;-&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;configuration&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Ljava/io/InputStream&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;Lcom/masabi/justride/sdk/AndroidJustRideSdkBuilder&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;548&lt;/span&gt;:    invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;p1&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;, Lcom/masabi/justride/sdk/AndroidJustRideSdkBuilder&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;-&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;build&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;Lcom/masabi/justride/sdk/AndroidJustRideSdk&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are: &lt;code&gt;TransitApp.smali&lt;/code&gt;, which seems like the entrypoint to the application. It seems like it passes some sort of &lt;code&gt;InputStream&lt;/code&gt; to the builder—maybe this is the JSON data we’re looking for?&lt;/p&gt;
&lt;p&gt;Here’s the method that invokes this (it’s just labeled &lt;code&gt;j&lt;/code&gt;, since we’re back into minified code):&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;.method&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;synthetic&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Throwable&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.locals&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;

    if-eqz &lt;span class=&quot;token register variable&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;cond_0&lt;/span&gt;

    return-void

    &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;cond_0&lt;/span&gt;
    const/4 &lt;span class=&quot;token register variable&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x0&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;try_start_0&lt;/span&gt;
    new-instance &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;io&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ByteArrayInputStream&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;B&lt;/span&gt;

    move-result-object &lt;span class=&quot;token register variable&quot;&gt;p1&lt;/span&gt;

    const/4 &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x0&lt;/span&gt;

    invoke-static &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;android&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Base64&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;BI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;B&lt;/span&gt;

    move-result-object &lt;span class=&quot;token register variable&quot;&gt;p1&lt;/span&gt;

    invoke-direct &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;io&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ByteArrayInputStream&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;&amp;#x3C;init&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;try_end_0&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.catch&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;try_start_0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;try_end_0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;catch_1&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.catchall&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;try_start_0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;try_end_0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;catchall_1&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;try_start_1&lt;/span&gt;
    invoke-static &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;masabi&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;justride&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;sdk&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AndroidJustRideSdk&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;masabi&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;justride&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;sdk&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AndroidJustRideSdkBuilder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    move-result-object &lt;span class=&quot;token register variable&quot;&gt;p1&lt;/span&gt;

    invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;masabi&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;justride&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;sdk&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AndroidJustRideSdkBuilder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;android&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Application&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;masabi&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;justride&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;sdk&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AndroidJustRideSdkBuilder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    move-result-object &lt;span class=&quot;token register variable&quot;&gt;p1&lt;/span&gt;

    invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;masabi&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;justride&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;sdk&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AndroidJustRideSdkBuilder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;io&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;InputStream&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;masabi&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;justride&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;sdk&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;AndroidJustRideSdkBuilder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token operator&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;..&lt;/span&gt;.]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Okay, so &lt;code&gt;v0&lt;/code&gt; is our &lt;code&gt;InputStream&lt;/code&gt;. We call &lt;code&gt;getBytes()&lt;/code&gt; on the string parameter, then Base64 decode it, then pass that into the &lt;code&gt;ByteArrayInputStream&lt;/code&gt; constructor. So where is the string passed into &lt;code&gt;j&lt;/code&gt;? Searching just that file (assuming it’s the entry point) for &lt;code&gt;-&gt;j&lt;/code&gt; gives another method, &lt;code&gt;b&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;.method&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;synthetic&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;thetransitapp&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;droid&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TransitApp&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Throwable&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.locals&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

    invoke-direct &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;thetransitapp&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;droid&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TransitApp&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Throwable&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;

    return-void
&lt;span class=&quot;token keyword&quot;&gt;.end&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;method&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It looks like this is a &lt;a href=&quot;https://www.baeldung.com/java-synthetic#1-bridge-methods&quot;&gt;bridge method&lt;/a&gt;, used to support generics.&lt;/p&gt;
&lt;p&gt;Now, is this method called anywhere? Doing a regex search in &lt;code&gt;invoke-static \{.*\}, Lcom/thetransitapp/droid/shared/TransitApp&lt;/code&gt; doesn’t find anything related to it.&lt;/p&gt;
&lt;h2 id=&quot;reading-smali-bytecode-working-down&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#reading-smali-bytecode-working-down&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Reading Smali Bytecode: Working Down&lt;/h2&gt;
&lt;p&gt;Maybe we missed something somewhere. Doing a bit of &lt;a href=&quot;https://stackoverflow.com/questions/10057448/entrypoint-of-android-application&quot;&gt;research&lt;/a&gt;, it looks like the entrypoint to an Android application is in a class that inherits from &lt;code&gt;Application&lt;/code&gt; and overrides the &lt;code&gt;onCreate&lt;/code&gt; method. Does our &lt;code&gt;TransitApp&lt;/code&gt; class fit the bill? Let’s check. Right at the top of the file, we see:&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;.class&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;thetransitapp&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;droid&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TransitApp&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;.super&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;android&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Application&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;.source&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TransitApp.java&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# interfaces&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;.implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;ac&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;b&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Okay, so that &lt;code&gt;.super&lt;/code&gt; line confirms it. Let’s look at the &lt;code&gt;onCreate()&lt;/code&gt; method. It’s quite long, so let’s break it down.&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;.method&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onCreate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;.locals&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;

    invoke-super &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;android&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Application&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onCreate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We start by calling the base &lt;code&gt;Application&lt;/code&gt;’s implementation of &lt;code&gt;onCreate()&lt;/code&gt; — pretty standard stuff for inheritance. We also have four local variables.&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;invoke-static &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;androidx&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;emoji2&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;c&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;android&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;androidx&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;emoji2&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;j&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;
const/4 &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x1&lt;/span&gt;
if-eqz &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;cond_0&lt;/span&gt;
invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;androidx&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;emoji2&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;e$c&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;androidx&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;emoji2&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;e$c&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;
invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;androidx&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;emoji2&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;e$c&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Z&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;androidx&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;emoji2&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;e$c&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;
new-instance &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;thetransitapp&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;droid&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TransitApp$a&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
invoke-direct &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;thetransitapp&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;droid&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TransitApp$a&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;&amp;#x3C;init&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;thetransitapp&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;droid&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TransitApp&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;
invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;androidx&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;emoji2&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;e$c&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;androidx&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;emoji2&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;e$e&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;androidx&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;emoji2&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;e$c&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;
invoke-static &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;androidx&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;emoji2&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;e&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;androidx&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;emoji2&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;e$c&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;androidx&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;emoji2&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;e&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;cond_0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.android.com/jetpack/androidx&quot;&gt;AndroidX&lt;/a&gt;, also known as Jetpack, is a set of Android libraries provided by Google to handle common tasks. &lt;a href=&quot;https://developer.android.com/jetpack/androidx/releases/emoji2&quot;&gt;&lt;code&gt;androidx.emoji2&lt;/code&gt;&lt;/a&gt; is a library to support modern emoji on older platforms, including text rendering and emoji pickers. The method calls here are minified, but we can safely rule this out as uninteresting for now. That said, the &lt;code&gt;if&lt;/code&gt; statement that seems to construct a new instance of &lt;code&gt;TransitApp&lt;/code&gt; definitely strikes me as odd.&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;android&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCacheDir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;io&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;File&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;
invoke-static &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;thetransitapp&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;droid&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NetworkHandler&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCacheDir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;io&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;File&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This seems to be setting the directory to store cached assets.&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;invoke-static &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;mb&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;a&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;android&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;android&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SharedPreferences&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;
iput-object &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;thetransitapp&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;droid&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TransitApp&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token field variable&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;android&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SharedPreferences&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code uses the &lt;a href=&quot;https://developer.android.com/reference/android/content/SharedPreferences&quot;&gt;SharedPreferences&lt;/a&gt; class in some form, likely to retrieve some form of user preferences and store a handle to them for later.&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;invoke-static &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;thetransitapp&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;droid&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;j2&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;android&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;I&lt;/span&gt;
move-result &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;
invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;android&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;android&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;res&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Resources$Theme&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;
invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;android&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;res&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Resources$Theme&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;applyStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;IZ&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This handles differentiating between dark and light theme.&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;invoke-direct &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;thetransitapp&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;droid&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TransitApp&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;
invoke-static &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;thetransitapp&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;droid&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;z2&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;android&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;
invoke-static &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;thetransitapp&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;droid&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;shared&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;util&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;z2&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;
invoke-static &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;google&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;firebase&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;crashlytics&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;FirebaseCrashlytics&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;google&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;firebase&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;crashlytics&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;FirebaseCrashlytics&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;
invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;google&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;firebase&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;crashlytics&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;FirebaseCrashlytics&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCrashlyticsCollectionEnabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Z&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;
invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;google&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;firebase&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;crashlytics&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;FirebaseCrashlytics&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setUserId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code grabs some sort of user ID, stores it in &lt;code&gt;v0&lt;/code&gt;, then uses it to set up the &lt;a href=&quot;https://firebase.google.com/docs/crashlytics&quot;&gt;Firebase Crashlytics&lt;/a&gt; crash reporter.&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;const-string &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;com.thetransitapp&quot;&lt;/span&gt;
filled-new-array &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;
invoke-static &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;z2&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;b&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;
new-instance &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
invoke-direct &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;&amp;#x3C;init&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;
invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;
invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;
invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;
invoke-static &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;a&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;d&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;
invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;d&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;f0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;k&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;d&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;
const-string &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3687b056476e15e4fe1b346e559a4169&quot;&lt;/span&gt;
invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;d&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;android&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;d&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;
invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;d&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;d&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;
invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;d&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;android&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Application&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;d&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;
const-wide/32 &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xea60&lt;/span&gt;
invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;d&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;c0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;J&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;d&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Checking the &lt;code&gt;a3/a.smali&lt;/code&gt; file, we get this header:&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;.class&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;a3&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;a&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;.super&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;.source&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Amplitude.java&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://www.docs.developers.amplitude.com/data/sdks/android/&quot;&gt;Amplitude&lt;/a&gt; looks like some kind of analytics application, which makes sense for something that would be set up in an &lt;code&gt;onCreate()&lt;/code&gt; call.&lt;/p&gt;
&lt;pre class=&quot;language-smali&quot; data-language=&quot;smali&quot;&gt;&lt;code class=&quot;language-smali&quot;&gt;const/4 &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x0&lt;/span&gt;
invoke-static &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;revenuecat&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;purchases&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Purchases&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setDebugLogsEnabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Z&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;
new-instance &lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;revenuecat&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;purchases&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PurchasesConfiguration$Builder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
const-string &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;JfhIYqEpBxRrLkgLLizTDhRqyoPguWdY&quot;&lt;/span&gt;
invoke-direct &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;p0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;revenuecat&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;purchases&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PurchasesConfiguration$Builder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;&amp;#x3C;init&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;android&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;V&lt;/span&gt;
invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;revenuecat&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;purchases&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PurchasesConfiguration$Builder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appUserID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;lang&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;revenuecat&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;purchases&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PurchasesConfiguration$Builder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;
invoke-virtual &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;revenuecat&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;purchases&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PurchasesConfiguration$Builder&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;revenuecat&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;purchases&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PurchasesConfiguration&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
move-result-object &lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;
invoke-static &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token register variable&quot;&gt;v0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;revenuecat&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;purchases&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Purchases&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;configure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;revenuecat&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;purchases&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PurchasesConfiguration&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token builtin&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;com&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;revenuecat&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;purchases&lt;span class=&quot;token punctuation&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Purchases&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code sets up the &lt;a href=&quot;https://www.revenuecat.com/&quot;&gt;RevenueCat&lt;/a&gt; integration for in-app subscriptions.&lt;/p&gt;
&lt;p&gt;The method continues on, but nothing else in it looks all that notable — just some error handling stuff. But looking into each of these integrations is making me realize: what exactly were we looking at before?&lt;/p&gt;
&lt;h2 id=&quot;so-what-exactly-is-justride&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#so-what-exactly-is-justride&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;So, what exactly is JustRide?&lt;/h2&gt;
&lt;p&gt;Remember, we first found our JustRide method in &lt;code&gt;com/masabi/justride/sdk/platform/AndroidPlatformModule2.smali&lt;/code&gt;. Are we sure that this is related to what we’re trying to find?&lt;/p&gt;
&lt;p&gt;Looking up &lt;a href=&quot;https://www.justride.com/&quot;&gt;JustRide&lt;/a&gt;, it advertizes itself as a mobile ticketing platform. They provide &lt;a href=&quot;https://www.masabi.com/justride-mobile-ticketing-sdk/&quot;&gt;an SDK&lt;/a&gt; for integrating ticket purchases into other apps. It seems like TransitApp just includes the JustRide SDK for this functionality, so its use of certificate pinning is largely irrelevant.&lt;/p&gt;
&lt;p&gt;Okay, back to the drawing board.&lt;/p&gt;
&lt;h2 id=&quot;network-security-config&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#network-security-config&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Network Security Config&lt;/h2&gt;
&lt;p&gt;Doing a bit more research, I stumbled upon a GitHub project called &lt;a href=&quot;https://github.com/levyitay/AddSecurityExceptionAndroid&quot;&gt;AddSecurityExceptionAndroid&lt;/a&gt; that claims to be able to enable reverse engineering. It links to a few Android reference pages.&lt;/p&gt;
&lt;p&gt;First, it mentions &lt;a href=&quot;https://developer.android.com/training/articles/security-config.html&quot;&gt;Network Security Configuration&lt;/a&gt;, which allows application developers to implement certificate pinning using a configuration file instead of any code changes. Maybe this is what we’re after? It gives an example &lt;code&gt;AndroidManifest.xml&lt;/code&gt; file for this:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot; data-language=&quot;xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token prolog&quot;&gt;&amp;#x3C;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;manifest&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;application&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;networkSecurityConfig&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@xml/network_security_config&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
                    &lt;span class=&quot;token attr-name&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        ...
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;application&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;manifest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s check this against TransitApp’s &lt;code&gt;AndroidManifest.xml&lt;/code&gt; to see if it uses this. I’ve grabbed only the &lt;code&gt;&amp;#x3C;application&gt;&lt;/code&gt; tag, since the file is quite large:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot; data-language=&quot;xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;application&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;allowBackup&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;appComponentFactory&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;androidx.core.app.CoreComponentFactory&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;extractNativeLibs&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;fullBackupContent&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@xml/backup_descriptor&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;icon&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@mipmap/ic_launcher&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@string/app_name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;largeHeap&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;localeConfig&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@xml/locales_config&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;logo&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@drawable/action_bar_icon&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;com.thetransitapp.droid.shared.TransitApp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;roundIcon&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@mipmap/ic_launcher_round&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;theme&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@style/SplashScreen&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;usesCleartextTraffic&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I don’t see a &lt;code&gt;networkSecurityConfig&lt;/code&gt; option, and furthermore, the app specifically opts into “cleartext traffic” (HTTP). Let’s keep looking.&lt;/p&gt;
&lt;p&gt;The other linked source is a &lt;a href=&quot;https://android-developers.googleblog.com/2016/07/changes-to-trusted-certificate.html&quot;&gt;blog post from 2016&lt;/a&gt;. Emphasis mine:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In Android Nougat, we’ve changed how Android handles trusted certificate authorities (CAs) to provide safer defaults for secure app traffic. Most apps and users should not be affected by these changes or need to take any action. The changes include:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Safe and easy APIs to trust custom CAs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Apps that target API Level 24 and above no longer trust user or admin-added CAs for secure connections, by default.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;All devices running Android Nougat offer the same standardized set of system CAs—no device-specific customizations.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;For more details on these changes and what to do if you’re affected by them, read on.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is the exact approach we were trying: adding our own CA to try to pull off a man-in-the-middle attack on ourselves.&lt;/p&gt;
&lt;p&gt;Reading further in the blog post, it seems like apps need to use a Network Security Config setting to opt into user-added CAs. Since TransitApp does not supply a Network Security Config, this is probably what’s blocking us from using our own CA here.&lt;/p&gt;
&lt;h2 id=&quot;addsecurityexceptionandroid&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#addsecurityexceptionandroid&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;AddSecurityExceptionAndroid&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;AddSecurityExceptionAndroid&lt;/code&gt; script works by overwriting the &lt;code&gt;network_security_config.xml&lt;/code&gt; file and adding the relevant attribute to the &lt;code&gt;application&lt;/code&gt; tag in &lt;code&gt;AndroidManifest.xml&lt;/code&gt;. Let’s run it on our &lt;code&gt;.apk&lt;/code&gt; and see if it works.&lt;/p&gt;
&lt;p&gt;First, set up a debug keystore in Android Studio, following &lt;a href=&quot;https://developer.android.com/studio/publish/app-signing#generate-key&quot;&gt;the documentation&lt;/a&gt;. I chose the following settings based on what I found in the &lt;code&gt;addSecurityExceptions.sh&lt;/code&gt; file:&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Setting&lt;/th&gt;&lt;th&gt;Value&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Key store path&lt;/td&gt;&lt;td&gt;&lt;code&gt;/Users/breq/.android/keystore.jks&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Password&lt;/td&gt;&lt;td&gt;&lt;code&gt;android&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Alias&lt;/td&gt;&lt;td&gt;&lt;code&gt;androiddebugkey&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Password&lt;/td&gt;&lt;td&gt;&lt;code&gt;android&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Validity&lt;/td&gt;&lt;td&gt;25 (default)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;After creating the key, you can exit out of Android Studio.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; clone https://github.com/levyitay/AddSecurityExceptionAndroid
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; AddSecurityExceptionAndroid
&lt;span class=&quot;token function&quot;&gt;cp&lt;/span&gt; ~/Downloads/Transit&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt; Bus&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt; Subway&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt; Times_5.13.5_Apkpure.apk ./transit.apk
./addSecurityExceptions.sh &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; transit.apk
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We use &lt;code&gt;-d&lt;/code&gt; to make the .apk debuggable — I don’t know if this will be useful or not, but there’s no reason &lt;em&gt;not&lt;/em&gt; to.&lt;/p&gt;
&lt;p&gt;When running this, I ran into this issue:&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;W: /tmp/transit/AndroidManifest.xml:82: error: attribute android:localeConfig not found.
W: error: failed processing manifest.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Based on this &lt;a href=&quot;https://github.com/iBotPeaches/Apktool/issues/2807&quot;&gt;GitHub issue&lt;/a&gt;, it seems like I need to make modifications to the APK before attempting to recompile it. I modified the &lt;code&gt;addSecurityException.sh&lt;/code&gt; script to wait before recompiling:&lt;/p&gt;
&lt;pre class=&quot;language-diff&quot; data-language=&quot;diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;@@ -122,6 +122,9 @@ if [ $makeDebuggable ] &amp;#x26;&amp;#x26; ! grep -q &quot;debuggable&quot; &quot;$tmpDir/AndroidManifest.xml&quot;;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;  mv &quot;$tmpDir/AndroidManifest.xml.new&quot; &quot;$tmpDir/AndroidManifest.xml&quot;
&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;fi
&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;echo &quot;Make any changes now in $tmpDir&quot;
&lt;/span&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;echo &quot;Press ENTER when done...&quot;
&lt;/span&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;read
&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;java -jar &quot;$DIR/apktool.jar&quot;  --use-aapt2 empty-framework-dir --force &quot;$tmpDir&quot;
&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;echo &quot;Building temp APK $tempFileName&quot;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, in another terminal, I opened the temporary directory (&lt;code&gt;/tmp/transit&lt;/code&gt; in my case). I didn’t see a &lt;code&gt;locales_config.xml&lt;/code&gt;, but I did notice that the &lt;code&gt;AndroidManifest.xml&lt;/code&gt; included the parameter &lt;code&gt;android:localeConfig=&quot;@xml/locales_config&quot;&lt;/code&gt;. I removed the &lt;code&gt;localeConfig&lt;/code&gt; parameter from &lt;code&gt;AndroidManifest.xml&lt;/code&gt;, then continued the &lt;code&gt;addSecurityException.sh&lt;/code&gt; script, which worked this time.&lt;/p&gt;
&lt;h2 id=&quot;dynamic-analysis-take-2&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#dynamic-analysis-take-2&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Dynamic Analysis: Take 2&lt;/h2&gt;
&lt;p&gt;We’ll need to uninstall the old app from our emulator before installing the APK. Boot up the emulated device and uninstall TransitApp, then drag and drop the new APK onto the emulator.&lt;/p&gt;
&lt;p&gt;Doing this, I get an error: &lt;code&gt;INSTALL_FAILED_NO_MATCHING_ABI: Failed to extract native libraries&lt;/code&gt;. Actually, when I try to use the unmodified &lt;code&gt;.apk&lt;/code&gt; I downloaded, I get the same error. Looking into &lt;code&gt;/tmp/transit/lib&lt;/code&gt;, we only see &lt;code&gt;x86_64&lt;/code&gt; — the sketchy website lied about what architecture the APK is.&lt;/p&gt;
&lt;p&gt;We can try &lt;a href=&quot;https://www.apkmirror.com/apk/transit-app-inc/transit-real-time-transit-app/transit-real-time-transit-app-5-14-6-release/transit-bus-subway-times-5-14-6-6-android-apk-download/&quot;&gt;another sketchy site&lt;/a&gt;. Looking in &lt;code&gt;/tmp/transit/lib&lt;/code&gt; now, we see &lt;code&gt;arm64-v8a&lt;/code&gt; — perfect. We can verify that this APK installs correctly. (Make sure you have internet access in your emulator — I forgot to launch &lt;code&gt;dnsmasq&lt;/code&gt; and had some issues with this.)&lt;/p&gt;
&lt;p&gt;We can go through the same steps of running the script and removing the &lt;code&gt;android:localeConfig&lt;/code&gt; parameter. Install this APK, and boom: our modified TransitApp build is running in our emulator. Some parts of the app don’t seem to be working: maybe this is because our proxy isn’t running?&lt;/p&gt;
&lt;p&gt;Start the proxy, verify the settings in &lt;code&gt;/etc/hosts&lt;/code&gt;, and restart &lt;code&gt;dnsmasq&lt;/code&gt; for good measure.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Holy shit. We’re finally getting something.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;heres-where-the-fun-begins&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#heres-where-the-fun-begins&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Here’s Where The Fun Begins&lt;/h2&gt;
&lt;p&gt;We can already see some requests just from opening the app and signing in. Here’s the response for &lt;code&gt;/v3/users/me&lt;/code&gt; right now:&lt;/p&gt;
&lt;pre class=&quot;language-http&quot; data-language=&quot;http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token response-status&quot;&gt;&lt;span class=&quot;token http-version property&quot;&gt;HTTP/2&lt;/span&gt; &lt;span class=&quot;token status-code number&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;token reason-phrase string&quot;&gt;OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;X-Powered-By&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Express&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;application/json; charset=utf-8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Content-Length&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;298&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Etag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;W/&quot;12a-pknx7teDN3n3f6xhsnm1RmASA/M&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Vary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Accept-Encoding&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Fri, 16 Jun 2023 22:29:36 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Via&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;1.1 google&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Alt-Svc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;h3=&quot;:443&quot;; ma=2592000,h3-29=&quot;:443&quot;; ma=2592000&lt;/span&gt;&lt;/span&gt;

{&quot;subscriptions&quot;:[],&quot;royale&quot;:{&quot;user_id&quot;:&quot;9702c35a82984e76&quot;,&quot;avatar&quot;:{&quot;username&quot;:&quot;Floppy Sensei&quot;,&quot;username_type&quot;:&quot;generated&quot;,&quot;image_id&quot;:&quot;💾&quot;,&quot;color&quot;:&quot;e3131b&quot;,&quot;foreground_color&quot;:&quot;ffffff&quot;,&quot;visibility&quot;:&quot;public&quot;}},&quot;main_agencies&quot;:[&quot;MBTA&quot;],&quot;time_zone_name&quot;:&quot;America/New_York&quot;,&quot;time_zone_delta&quot;:&quot;-0400&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s where we can confirm our hypothesis about the emoji shuffle being done client-side. Looking at the network logs while clicking “shuffle,” nothing seems to be happening in the network console. Perfect!&lt;/p&gt;
&lt;p&gt;We’ll edit our username and icon by accepting one of the suggestions. Here’s the generated request (I’ve changed the User ID and removed the authorization token):&lt;/p&gt;
&lt;pre class=&quot;language-http&quot; data-language=&quot;http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token request-line&quot;&gt;&lt;span class=&quot;token method property&quot;&gt;PATCH&lt;/span&gt; &lt;span class=&quot;token request-target url&quot;&gt;/v3/users/b1dfb2047e8bd5eb&lt;/span&gt; &lt;span class=&quot;token http-version property&quot;&gt;HTTP/2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;api.transitapp.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Accept-Language&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;en-US&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Authorization&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Basic [REDACTED]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Transit-Hours-Representation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;12&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;User-Agent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Transit/20900 transitLib/114 Android/13 Device/sdk_gphone64_arm64 Version/5.14.6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;application/json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Content-Length&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;196&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Keep-Alive&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Accept-Encoding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;gzip, deflate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token application-json&quot;&gt;

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;avatar&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;color&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;f3a4ba&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;foreground_color&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;804660&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;image_id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;🍦&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;image_type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;emoji&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;subscribed&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;username&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Cone Extravaganza&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;username_type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;generated&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;visibility&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;public&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here’s the response:&lt;/p&gt;
&lt;pre class=&quot;language-http&quot; data-language=&quot;http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token response-status&quot;&gt;&lt;span class=&quot;token http-version property&quot;&gt;HTTP/2&lt;/span&gt; &lt;span class=&quot;token status-code number&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;token reason-phrase string&quot;&gt;OK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;X-Powered-By&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Express&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;application/json; charset=utf-8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Content-Length&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;180&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Etag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;W/&quot;b4-0vP9yEjHVjjz1iTkW1gCoYEcQG4&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Vary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Accept-Encoding&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Fri, 16 Jun 2023 22:32:01 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Via&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;1.1 google&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Alt-Svc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;h3=&quot;:443&quot;; ma=2592000,h3-29=&quot;:443&quot;; ma=2592000&lt;/span&gt;&lt;/span&gt;

{&quot;id&quot;:&quot;b1dfb2047e8bd5eb&quot;,&quot;avatar&quot;:{&quot;color&quot;:&quot;f3a4ba&quot;,&quot;foreground_color&quot;:&quot;804660&quot;,&quot;username&quot;:&quot;Cone Extravaganza&quot;,&quot;username_type&quot;:&quot;generated&quot;,&quot;image_id&quot;:&quot;🍦&quot;,&quot;visibility&quot;:&quot;public&quot;}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s modify the request a bit. Turn on Burp’s Intercept tool, then randomize the name and icon again:&lt;/p&gt;
&lt;pre class=&quot;language-http&quot; data-language=&quot;http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token request-line&quot;&gt;&lt;span class=&quot;token method property&quot;&gt;PATCH&lt;/span&gt; &lt;span class=&quot;token request-target url&quot;&gt;/v3/users/b1dfb2047e8bd5eb&lt;/span&gt; &lt;span class=&quot;token http-version property&quot;&gt;HTTP/2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;api.transitapp.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Accept-Language&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;en-US&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Authorization&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Basic [REDACTED]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Transit-Hours-Representation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;12&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;User-Agent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Transit/20900 transitLib/114 Android/13 Device/sdk_gphone64_arm64 Version/5.14.6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;application/json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Content-Length&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;191&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Keep-Alive&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Accept-Encoding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;gzip, deflate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token application-json&quot;&gt;

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;avatar&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;color&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ffce00&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;foreground_color&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;855323&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;image_id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;😐&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;image_type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;emoji&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;subscribed&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;username&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Feral Voovie&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;username_type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;generated&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;visibility&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;public&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s quickly swap out the payload for this:&lt;/p&gt;
&lt;pre class=&quot;language-http&quot; data-language=&quot;http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token request-line&quot;&gt;&lt;span class=&quot;token method property&quot;&gt;PATCH&lt;/span&gt; &lt;span class=&quot;token request-target url&quot;&gt;/v3/users/b1dfb2047e8bd5eb&lt;/span&gt; &lt;span class=&quot;token http-version property&quot;&gt;HTTP/2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;api.transitapp.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Accept-Language&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;en-US&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Authorization&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Basic [REDACTED]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Transit-Hours-Representation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;12&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;User-Agent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Transit/20900 transitLib/114 Android/13 Device/sdk_gphone64_arm64 Version/5.14.6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;application/json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Content-Length&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;191&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Keep-Alive&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Accept-Encoding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;gzip, deflate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token application-json&quot;&gt;

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;avatar&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;color&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ff42a1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;foreground_color&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;000000&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;image_id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;🎈&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;image_type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;emoji&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;subscribed&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;username&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Brooke Chalmers&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;username_type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;generated&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;visibility&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;public&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Things didn’t update in the UI. However, if we sign out and sign back in… There we go!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1080&quot; height=&quot;2400&quot; src=&quot;https://breq.dev/_astro/success.DGw6pKfL_Z2pAFsX.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Thanks for joining me on this little adventure! I worked on this blog post every now and then over the course of a few months. Getting this to work in the end was such a great feeling — if my initial hypothesis had been wrong, I still would’ve learned a lot, but the payoff would’ve been quite a bit less fun. And if you see a 🎈 emoji rider around Boston, feel free to say hi!&lt;/p&gt;</content:encoded></item><item><title>Emulation Project - Call for Collaborators!</title><link>https://breq.dev/2022/11/26/noentiendo/</link><guid isPermaLink="true">https://breq.dev/2022/11/26/noentiendo/</guid><description>Something exciting I&apos;ve had in the works, and how you can help!</description><pubDate>Sat, 26 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’m trying to emulate a bunch of 6502-based systems — the Commodore PET, VIC-20, and 64, the Apple IIe, and the NES. I’m writing it in Rust, currently targeting desktop and WebAssembly but with plans to support mobile and embedded, too. Right now, I’ve got the PET working, and you can try it out &lt;a href=&quot;https://breq.dev/noentiendo/index.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is an ambitious project, and I’m seeing some exciting results, but &lt;strong&gt;I need your help&lt;/strong&gt; if I’m going to have a chance at getting all of this working within a reasonable timeframe. If you like Rust, are interested in old hardware, and might have some free time soon, &lt;a href=&quot;https://breq.dev/contact&quot;&gt;let me know&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;A screenshot of a Commodore PET running BASIC.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1504&quot; height=&quot;1080&quot; src=&quot;https://breq.dev/_astro/pet.ukqcCNzY_Z2bVgBL.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;the-premise&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-premise&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Premise&lt;/h1&gt;
&lt;p&gt;The MOS 6502 defines an era of retro computing. It was a powerful chip, priced incredibly low. This same chip was used in the Commodore 64, Apple IIe, BBC Micro, and others, and a knockoff (with an entirely compatible instruction set) was used in the Nintendo Entertainment System. Many of these computers also relied on the MOS 6520 (Peripheral Interface Adapter, or PIA) and the MOS 6522 (Versatile Interface Adapter, or VIA).&lt;/p&gt;
&lt;p&gt;Rust, like C or C++, is notable for its wide range of potential platforms. Toolchains exist for desktop, web, mobile, and even embedded processors, leaving open the possibility of creating a handheld device built to run this emulator.&lt;/p&gt;
&lt;p&gt;By writing an emulator that works off of a few basic building blocks — the 6502, the PIA and VIA, some basic RAM and ROM, and some special functions like the VIC chip — it should be possible to emulate a wide range of systems, sharing much of the code between them. And by implementing this in Rust and targeting a variety of platforms, it should be possible to emulate anything, anywhere.&lt;/p&gt;
&lt;h1 id=&quot;the-work-so-far&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-work-so-far&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Work So Far&lt;/h1&gt;
&lt;p&gt;If you prefer reading code, take a look at &lt;a href=&quot;https://github.com/breqdev/noentiendo&quot;&gt;the repo&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;--system-brooke&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#--system-brooke&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;--system brooke&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The first system implementation was just something I came up with for testing. A &lt;code&gt;System&lt;/code&gt; represents the 6502 and some attached &lt;code&gt;Memory&lt;/code&gt;, which in this case was just RAM from &lt;code&gt;0x0000&lt;/code&gt; through &lt;code&gt;0x3FFF&lt;/code&gt;, some memory-mapped I/O at &lt;code&gt;0x4000&lt;/code&gt;, and just ROM from &lt;code&gt;0x8000&lt;/code&gt; to &lt;code&gt;0xFFFF&lt;/code&gt;. I chose this configuration to be similar to &lt;a href=&quot;https://eater.net/6502&quot;&gt;Ben Eater’s homemade computer&lt;/a&gt;&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, so that I could use his toolchain and examples to test my processor.&lt;/p&gt;
&lt;p&gt;Here’s some of the assembly I wrote by hand in the early days. This program will capitalize any letter it receives:&lt;/p&gt;
&lt;pre class=&quot;language-asm6502&quot; data-language=&quot;asm6502&quot;&gt;&lt;code class=&quot;language-asm6502&quot;&gt;MAPPED_STDIO = &lt;span class=&quot;token hex-number number&quot;&gt;$4001&lt;/span&gt;

  &lt;span class=&quot;token directive property&quot;&gt;.org&lt;/span&gt; &lt;span class=&quot;token hex-number number&quot;&gt;$8000&lt;/span&gt;

reset&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token op-code keyword&quot;&gt;LDA&lt;/span&gt; MAPPED_STDIO
  &lt;span class=&quot;token op-code keyword&quot;&gt;CMP&lt;/span&gt; &lt;span class=&quot;token hex-number number&quot;&gt;#$61&lt;/span&gt;
  &lt;span class=&quot;token op-code keyword&quot;&gt;BMI&lt;/span&gt; skip_capitalize
  &lt;span class=&quot;token op-code keyword&quot;&gt;CMP&lt;/span&gt; &lt;span class=&quot;token hex-number number&quot;&gt;#$7B&lt;/span&gt;
  &lt;span class=&quot;token op-code keyword&quot;&gt;BPL&lt;/span&gt; skip_capitalize

  &lt;span class=&quot;token op-code keyword&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;token hex-number number&quot;&gt;#$DF&lt;/span&gt;

skip_capitalize&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token op-code keyword&quot;&gt;STA&lt;/span&gt; MAPPED_STDIO
  &lt;span class=&quot;token op-code keyword&quot;&gt;JMP&lt;/span&gt; reset

  &lt;span class=&quot;token directive property&quot;&gt;.org&lt;/span&gt; &lt;span class=&quot;token hex-number number&quot;&gt;$fffa&lt;/span&gt;
vectors&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token directive property&quot;&gt;.word&lt;/span&gt; &lt;span class=&quot;token hex-number number&quot;&gt;$0000&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;; NMI&lt;/span&gt;
  &lt;span class=&quot;token directive property&quot;&gt;.word&lt;/span&gt; reset&lt;span class=&quot;token comment&quot;&gt;; RESET&lt;/span&gt;
  &lt;span class=&quot;token directive property&quot;&gt;.word&lt;/span&gt; &lt;span class=&quot;token hex-number number&quot;&gt;$0000&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;; IRQ&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;--platform-text&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#--platform-text&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;--platform text&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;How does the above program actually read and write text? This is where &lt;code&gt;Platform&lt;/code&gt;s come in. The &lt;code&gt;Platform&lt;/code&gt; trait provides platform-specific code to run the &lt;code&gt;System&lt;/code&gt;, and each &lt;code&gt;Memory&lt;/code&gt; object can keep a &lt;code&gt;PlatformProvider&lt;/code&gt; to provide functionality like writing to the terminal, prompting for input, drawing a pixel on the screen, or checking which keys are pressed.&lt;/p&gt;
&lt;p&gt;The Text platform is the simplest, only providing read and write capabilities through the terminal.&lt;/p&gt;
&lt;h2 id=&quot;--system-klaus&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#--system-klaus&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;--system klaus&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;To verify that every opcode of my 6502 implementation worked, I decided to use &lt;a href=&quot;https://github.com/Klaus2m5/6502_65C02_functional_tests&quot;&gt;Klaus Dormann’s functional tests&lt;/a&gt;. This &lt;code&gt;System&lt;/code&gt; was the harness that let me run these tests.&lt;/p&gt;
&lt;h2 id=&quot;--system-easy&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#--system-easy&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;--system easy&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Another project I leaned off of was the &lt;a href=&quot;https://skilldrick.github.io/easy6502/&quot;&gt;Easy6502&lt;/a&gt; guide. This guide walks you through writing a game of Snake for a bespoke 6502 system which outputs to a 16x16 color display. I implemented this video system for my emulator, and ran the example implementation of Snake. This basic 16x16 display was substantially more simple than any real-world video circuit, so it made a perfect first implementation.&lt;/p&gt;
&lt;h2 id=&quot;--platform-winit&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#--platform-winit&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;--platform winit&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;To actually push pixels to the screen, I landed on using &lt;a href=&quot;https://github.com/parasyte/pixels&quot;&gt;&lt;code&gt;pixels&lt;/code&gt;&lt;/a&gt; to plot pixels and &lt;a href=&quot;https://github.com/rust-windowing/winit/&quot;&gt;&lt;code&gt;winit&lt;/code&gt;&lt;/a&gt; to handle creating the window and handling keyboard events. I initially tried using &lt;a href=&quot;https://github.com/emoon/rust_minifb&quot;&gt;&lt;code&gt;minifb&lt;/code&gt;&lt;/a&gt; to handle both tasks, but I found it to have slightly worse performance.&lt;/p&gt;
&lt;h2 id=&quot;--system-pet&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#--system-pet&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;--system pet&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;My next step was to actually implement a real computer. I chose the Commodore PET, since its simple monochrome text-mode graphics would be relatively easy to implement.&lt;/p&gt;
&lt;p&gt;In the PET, text is placed on the screen by writing a specific character code to a specific location in the video memory. There is no color support, bitmap mode, or other frills — it’s just text mode.&lt;/p&gt;
&lt;p&gt;The PET also uses a PIA chip, which I needed to implement. This is used to read the keyboard row, and to receive a 60Hz interrupt from the video circuitry.&lt;/p&gt;
&lt;p&gt;I did still have to implement the keyboard, which proved slightly difficult. The keyboard layout on the PET’s “graphics keyboard” (one of the two standard keyboards for the PET) is quite different from a modern computer keyboard. Notably, it places the double-quote &lt;kbd&gt;”&lt;/kbd&gt; on a key which does not require &lt;kbd&gt;Shift&lt;/kbd&gt; to be pressed. After adding that and a few other special cases, it just required implementing the keyboard matrix scan logic to return the correct bits for each keyboard row.&lt;/p&gt;
&lt;h2 id=&quot;--target-wasm32-unknown-unknown&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#--target-wasm32-unknown-unknown&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;--target wasm32-unknown-unknown&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;A screenshot of a Commodore PET emulator running in a browser.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1672&quot; height=&quot;1316&quot; src=&quot;https://breq.dev/_astro/wasm.DnUbRPiA_1nsiL3.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;This is when I added support for WebAssembly. In a browser, the emulator draws to a &lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt; element, also using &lt;code&gt;winit&lt;/code&gt;. (I’m thinking of transitioning away from &lt;code&gt;winit&lt;/code&gt; and just directly setting up the &lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt; through JavaScript bindings.) The Easy6502 implementation works fine, and so does the PET. (The text-mode stuff also works, albeit through &lt;code&gt;alert()&lt;/code&gt; and &lt;code&gt;input()&lt;/code&gt; calls.)&lt;/p&gt;
&lt;h2 id=&quot;--system-vic&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#--system-vic&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;--system vic&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;A screenshot of a VIC-20 displaying the BASIC startup screen.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;928&quot; height=&quot;1016&quot; src=&quot;https://breq.dev/_astro/vic.BHcbkmMq_ZQ1L88.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;My most recent work has been trying to emulate the VIC-20. The VIC-20 is named after the &lt;em&gt;VIC chip&lt;/em&gt;, or the Video Interface Chip. (Specifically, it’s the MOS 6560 or 6561 in NTSC and PAL regions respectively.) This chip manages the background and border colors, the sound output, the light pen, and a few other miscellaneous video-related features.&lt;/p&gt;
&lt;p&gt;The VIC-20 also trades the PIAs for VIAs. Although the PET contained a VIA, it was only used for the IEEE-488 interface (used for disk/tape drives and storage), so I didn’t implement it. The VIC-20 uses its VIAs for reading the keyboard state and for setting up a 60Hz timer, both of which are required to get a minimal working system.&lt;/p&gt;
&lt;p&gt;The VIC-20 uses three separate areas of memory for video-related functions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;em&gt;screen memory&lt;/em&gt; stores what character is displayed at each position on the screen.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;character memory&lt;/em&gt; stores the shape of each character — kind of like the “font” of the system.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;color memory&lt;/em&gt; stores the color code for each position on the screen.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;This work is ongoing.&lt;/strong&gt; At time of writing, the system boots to the startup screen (with color), but fails to blink the cursor or display typed characters. Work is ongoing in the &lt;code&gt;bc/vic-20&lt;/code&gt; branch of the repo.&lt;/p&gt;
&lt;h1 id=&quot;the-road-ahead&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-road-ahead&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Road Ahead&lt;/h1&gt;
&lt;p&gt;My immediate goal is to get the VIC-20 working, which should happen soon. Past that, and loosely in order of priority, here’s what I want to tackle:&lt;/p&gt;
&lt;h2 id=&quot;emulating-disk-drives&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#emulating-disk-drives&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Emulating Disk Drives&lt;/h2&gt;
&lt;p&gt;Currently, the PET can only be used for running programs that you’re willing to type out at the BASIC interpreter. Emulating a disk drive will make it easier to load a wide array of software, increasing the utility of the emulator and helping to test other parts of the system. Notably, lots of Commodore machines used the same drives, which might make this easy.&lt;/p&gt;
&lt;h2 id=&quot;cleaning-up-the-webassembly-experience&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#cleaning-up-the-webassembly-experience&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Cleaning Up the WebAssembly Experience&lt;/h2&gt;
&lt;p&gt;Right now, the WebAssembly build is a somewhat manual process built on top of &lt;code&gt;wasm-pack&lt;/code&gt;, with no automated deployment. Additionally, swapping between systems requires commenting out system-specific code.&lt;/p&gt;
&lt;p&gt;I’d like the WebAssembly experience to be user-friendly enough for a user to select the system they would like to run from their browser. I’d also like deployment to be more automated, so pushes to the Git repository will trigger the web deployment to be up to date. This might also be a good time to remove the &lt;code&gt;winit&lt;/code&gt; dependency for WebAssembly, and to work with the &lt;code&gt;&amp;#x3C;canvas&gt;&lt;/code&gt; directly. (That would also let us attach event listeners to the page itself, not just the canvas.)&lt;/p&gt;
&lt;h2 id=&quot;the-commodore-64&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-commodore-64&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Commodore 64&lt;/h2&gt;
&lt;p&gt;I’d like to emulate the Commodore 64, due to its popularity. The only substantial difference it has to the VIC-20 is the video circuitry, so once the VIC-20 is working, this should be a pretty easy system to get running.&lt;/p&gt;
&lt;p&gt;Notably, the Commodore 64 uses the MOS 6510, not the 6502. This adds an 8-bit I/O port.&lt;/p&gt;
&lt;h2 id=&quot;native-mobile-apps&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#native-mobile-apps&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Native Mobile Apps&lt;/h2&gt;
&lt;p&gt;While mobile users &lt;em&gt;could&lt;/em&gt; use the WebAssembly version, the low performance of mobile devices means that the overhead of WASM makes the experience laggy. A native mobile app could also give a better keyboard experience for users.&lt;/p&gt;
&lt;h2 id=&quot;the-apple-iie&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-apple-iie&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Apple IIe&lt;/h2&gt;
&lt;p&gt;This was another popular 6502-based computer with a rich software library. It has less in common with the Commodore machines, meaning it might be more difficult to get working.&lt;/p&gt;
&lt;h2 id=&quot;embedded-design-sketching&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#embedded-design-sketching&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Embedded Design Sketching&lt;/h2&gt;
&lt;p&gt;My vision is to have some physical device with physical controls and a physical display to run the emulator as firmware. I don’t intend on bringing this to market, partly because I don’t think there is enough demand and partly because we would have to be careful to avoid copyright issues (e.g. the kernals of the Commodore machines are still protected under copyright). That said, I want it to be inexpensive enough that we could put together a few as a proof-of-concept.&lt;/p&gt;
&lt;p&gt;Figuring out the vision for this project requires:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Choosing a chip
&lt;ul&gt;
&lt;li&gt;Ideally we’d want one with good Rust support, like the RP2040&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Choosing a display
&lt;ul&gt;
&lt;li&gt;This might be the most expensive part of the system&lt;/li&gt;
&lt;li&gt;We’d want something with color, and a good “middle ground” aspect ratio&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Drawing a schematic&lt;/li&gt;
&lt;li&gt;Laying out a PCB&lt;/li&gt;
&lt;li&gt;Assembly!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-nintendo-entertainment-system&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-nintendo-entertainment-system&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Nintendo Entertainment System&lt;/h2&gt;
&lt;p&gt;The NES has a complicated video system with hardware sprites and multiple modes. It will be quite a challenge to implement. It uses the Ricoh 2A03, which is a 6502 clone but doesn’t have Binary Coded Decimal support for patent-related reasons.&lt;/p&gt;
&lt;h2 id=&quot;cleaning-up-the-desktop-experience&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#cleaning-up-the-desktop-experience&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Cleaning Up the Desktop Experience&lt;/h2&gt;
&lt;p&gt;It might be nice to have a user-friendly GUI that allows users to choose their system and ROM before launching. It also might be nice to ship a compiled, signed executable for all platforms.&lt;/p&gt;
&lt;h2 id=&quot;additional-systems&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#additional-systems&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Additional Systems&lt;/h2&gt;
&lt;p&gt;Other potential 6502-bsed systems include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Apple I&lt;/em&gt;, other members of the &lt;em&gt;Apple II&lt;/em&gt; family&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Acorn&lt;/em&gt;’s various Eurocard systems&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Atari&lt;/em&gt;’s 8-bit family including the &lt;em&gt;Atari 400&lt;/em&gt; and &lt;em&gt;800&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;support-for-additional-cpus&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#support-for-additional-cpus&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Support for additional CPUs&lt;/h2&gt;
&lt;p&gt;In the long term, it might be nice to add support for additional CPUs. Potential candidates include:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WDC 65C02, WDC 65C816, Ricoh 5A22:&lt;/strong&gt; This family was based on the original 6502. The 65C02 removed some undocumented opcodes, added some new opcodes, and fixed some errata from the old silicon. The 65C816 made even more extensions, including 16-bit registers, but maintains binary compatibility with the 6502. Finally, the Ricoh 5A22 is a clone of the 65C816, similar to how the Ricoh 2A03 clones the 6502.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WDC 65C02: &lt;em&gt;Apple IIc&lt;/em&gt;, &lt;em&gt;Enhanced Apple IIe&lt;/em&gt;, &lt;em&gt;BBC Master&lt;/em&gt;, &lt;em&gt;Atari Lynx&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;WDC 65C816: &lt;em&gt;Apple IIGS&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Ricoh 5A22: &lt;em&gt;Super Nintendo Entertainment System&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;8080, Z80, “GB-Z80”, 8086:&lt;/strong&gt; This family of processors was also widely used. The Z80 is an extension of the Intel 8080, and the “GB-Z80” (technically a Sharp LR35902) shares many of the same opcodes. The Intel 8086 has similar opcodes to the 8080.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Intel 8080: &lt;em&gt;Altair 8800&lt;/em&gt;, &lt;em&gt;Sol-20&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Zilog Z80: &lt;em&gt;ZX Spectrum&lt;/em&gt; (and the &lt;em&gt;ZX 80&lt;/em&gt; and &lt;em&gt;ZX 81&lt;/em&gt;), &lt;em&gt;TRS-80&lt;/em&gt;, &lt;em&gt;Cambridge Computer Z88&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;“GB-Z80” / Sharp LR35902: &lt;em&gt;Game Boy&lt;/em&gt;, &lt;em&gt;Game Boy Color&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Intel 8086: &lt;em&gt;IBM PC (model 5150)&lt;/em&gt;, &lt;em&gt;IBM PS/2&lt;/em&gt;, &lt;em&gt;Tandy 1000&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Motorola 68000&lt;/strong&gt; This is a 16/32-bit processor with a 32-bit instruction set and a 16-bit data bus. It was used in the &lt;em&gt;Macintosh&lt;/em&gt;, &lt;em&gt;Amiga&lt;/em&gt;, &lt;em&gt;Atari ST&lt;/em&gt;, &lt;em&gt;Sun-1&lt;/em&gt;, &lt;em&gt;Apple Lisa&lt;/em&gt;, &lt;em&gt;Sinclair QL&lt;/em&gt;, and &lt;em&gt;Sega Genesis&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;project-name&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#project-name&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Project name?&lt;/h2&gt;
&lt;p&gt;So far, I’ve just been calling the project “noentiendo,” as a pun on Nintendo and an allusion to the fact that I didn’t know much about Rust or retro computing before starting this project. I’ve been thinking about calling it “MoxEMU,” since I really like Moxie soda. I’d love other suggestions — maybe one will stick!&lt;/p&gt;
&lt;h1 id=&quot;timeline&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#timeline&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Timeline&lt;/h1&gt;
&lt;p&gt;My immediate priority is getting the VIC-20 working. I don’t have an ETA on when that’ll be finish, but my hope is that it’ll be done by New Years. After that branch gets merged, I’d love to start working on this with a group of people. Hopefully, once the initial design is frozen, collaboration should be easy due to the modular nature of the system.&lt;/p&gt;
&lt;p&gt;If you’re interested, get in touch with me, and I can keep you up to date!&lt;/p&gt;
&lt;section data-footnotes=&quot;&quot; class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;group&quot; id=&quot;footnote-label&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#footnote-label&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;It’s also a big part of what inspired me to start this project! &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>Dynamic Music</title><link>https://breq.dev/projects/dynamic-music/</link><guid isPermaLink="true">https://breq.dev/projects/dynamic-music/</guid><description>Navigate and manipulate a virtual environment of sound sources.</description><pubDate>Fri, 13 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1920&quot; height=&quot;1080&quot; src=&quot;https://breq.dev/_astro/dynamic-music.B2kgauRL_Z1ELmlH.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;A screenshot of the environment.&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;This project is a virtual environment containing several sound sources, represented as spheres. The listener, represented with a cone, can navigate around the environment to hear different combinations of the sources. Additionally, they can move the sources around within the environment.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I was inspired by the way it feels to work with music in a DAW: almost like exploring some sort of space. I decided to create a virtual space which replicated that feeling, allowing anyone to play with the mixing of a song. I also made this out of a desire to work with &lt;a href=&quot;https://threejs.org/&quot;&gt;Three.js&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The app is written in vanilla JS using &lt;a href=&quot;https://vitejs.dev/&quot;&gt;Vite&lt;/a&gt; for build tooling. Each audio track was a &lt;code&gt;wav&lt;/code&gt; file exported from an Ableton Live set. I decided to take an object-oriented approach to the code layout, representing each sound object with an ES5 class.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;Aside from a, uh, &lt;a href=&quot;https://twitter.com/threejs/status/1484518641098014722&quot;&gt;learning moment&lt;/a&gt; causing some issues with the audio panning, most of the project was straightforward. In the end, I think I succeeded at creating the environment I set out to create, although a bit more variety in the sound sources (perhaps multiple sections of the piece which could be alternated between?) might have helped the experience not bore the listener as quickly.&lt;/p&gt;</content:encoded></item><item><title>MIDI LiDAR</title><link>https://breq.dev/projects/midilidar/</link><guid isPermaLink="true">https://breq.dev/projects/midilidar/</guid><description>Control a digital musical instrument by moving your hands in free space.</description><pubDate>Fri, 13 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;youtube id=&quot;mgYmrErOh_Y&quot;&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;This project is a new type of MIDI controller which uses a LiDAR sensor to detect the positions of the user’s hands within two arbitrary zones, then maps this input to four separate MIDI streams. Each stream can send either note values to control pitch or Continuous Controller (CC) messages to control other parameters in a DAW.&lt;/p&gt;
&lt;p&gt;This controller uses a single sensor placed in the middle of a playing surface (desk, table, etc). The performer sits or stands such that the sensor is directly in front of their body. On either side of the sensor, zones are marked out in software. These zones can be chosen to align with physical references, such as a printed grid. The performer positions their hands within these zones in the air above the playing surface. Then, the horizontal and vertical position of each hand is used to generate MIDI messages, which control some sound source.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;4000&quot; height=&quot;3000&quot; src=&quot;https://breq.dev/_astro/midilidar.CnncGC0u_Z1i6hId.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;The sensor located on my desk, with two reference sheets corresponding to two zones.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;My primary inspiration for this project was the theremin. I had the opportunity to play one when I was young, and I was amazed by how intuitive and inviting the instrument felt. I was quickly able to get a sense for how the pitch and volume were controlled, but I still had the sense that there were so many possibilities for the device despite the simple controls. I was also struck by how inviting the device felt. Generally, when I see an unfamiliar instrument, my first reaction is to hold my hand tentatively over it and ask, “Can I play this?” With the theremin, simply being in the same vicinity affects the sound, and when I curiously held my hand near it, I found I was already playing.&lt;/p&gt;
&lt;p&gt;Although some highly skilled musicians such as Clara Rockmore developed &lt;a href=&quot;https://zwentzen.files.wordpress.com/2010/10/thereminmethod.pdf&quot;&gt;techniques&lt;/a&gt; for the theremin that increased its versatility, the instrument is often used simply for “spooky sound effects” instead of pitched music. The &lt;a href=&quot;https://www.thomasbloch.net/en_ondes-martenot.html&quot;&gt;ondes Martenot&lt;/a&gt;, a later electronic instrument using a continuous wire, had a non-functional image of a piano keyboard which provided a reference point. This instrument was just as continuous as a theremin, but the reference keyboard made it more widely used in pitched music. It also allowed for multiple configurations of the resonance diffuser, giving the instrument a wider range of timbre.&lt;/p&gt;
&lt;p&gt;A more modern inspiration was &lt;a href=&quot;https://doi.org/10.21428/92fbeb44.761367fd&quot;&gt;Quadrant&lt;/a&gt;, a MIDI controller developed by Chris Chronopoulos from 2018 through 2021. This controller uses upward-facing time-of-flight sensors to determine the pose of the player’s hand. Notably, it features a variety of different control modes: using four sensors, it can detect the position, velocity, orientation, and angular velocity of the performer’s hand. It also features special modes which can detect sweeps of the hand across the instrument, or plucking motions made above each of the four sensors. Of course, being a MIDI controller, it can be used to control an incredible variety of digital sound sources.&lt;/p&gt;
&lt;p&gt;I was also inspired by the &lt;a href=&quot;https://www.laserspectacles.com/resources/the-laser-harp/&quot;&gt;laser harp&lt;/a&gt;, another modern MIDI controller played without contact. Again, I appreciated how intuitive the device seemed: by leveraging the existing understanding that people have of traditional harps, the laser harp makes its controls obvious.&lt;/p&gt;
&lt;p&gt;With this project, I wanted to create a controller that operated in free space, just like the theremin. I wanted to give a reference to allow for more precise pitch control, like the keyboard provided by the ondes Martenot. I wanted to support various control modes, in a manner similar to Quadrant, and I wanted the device to leverage existing intuitions people have about theremins in the same way the laser harp leverages existing intuitions about harps. Above all, I wanted this controller to be as intuitive and inviting as the theremin felt to me when I was younger.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;h2 id=&quot;lidar-sensor&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#lidar-sensor&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;LiDAR Sensor&lt;/h2&gt;
&lt;p&gt;For this project, I am using a LiDAR sensor, specifically the &lt;a href=&quot;https://cdn-shop.adafruit.com/product-files/4010/4010_datasheet.pdf&quot;&gt;Slamtec RPLIDAR A1&lt;/a&gt;. This sensor is used to detect the position of the user’s hands in free space. It features an infrared laser diode and a receiver, mounted on a spinning platform.&lt;/p&gt;
&lt;p&gt;This sensor is a type of &lt;a href=&quot;https://learn.adafruit.com/slamtec-rplidar-on-pi?view=all&quot;&gt;Time-of-Flight (ToF)&lt;/a&gt; sensor. It functions by sending out a pulse of infrared laser light and measuring the time taken for the light to bounce off of an object and return to the receiver. Stationary ToF sensors, like those used on Quadrant, produce a stream of distance values:&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;[...]
0.505452 m
0.501343 m
0.499432 m
0.476832 m
[...]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each of these values represents the distance from the sensor to the nearest object it sees at a specific instant in time. The range of these sensors varies: the sensors used on Quadrant had a range of about 1 meter, while this sensor can measure up to 12 meters. As there are no external factors that drastically affect the speed of light, these readings are generally quite accurate.&lt;/p&gt;
&lt;p&gt;By mounting the ToF sensor onto a rotating platform, it is possible to measure at many different angles and construct a 2D map of the sensor’s surroundings. For this reason, these sensors are often used in robotics, to allow a mobile robot to map its environment, determine its position, and navigate. While LiDAR sensors are used in some autonomous driving applications, they are most commonly used in some high-end robot vacuum cleaners to map and clean a given area. For this MIDI controller, application however, the sensor is kept stationary.&lt;/p&gt;
&lt;p&gt;By combining the distance measurements from the laser diode with the angle of the rotating platform at the time of measurement, the sensor constructs a stream of angle/distance pairs.&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;[...]
30.12° 0.505452 m
31.10° 0.501343 m
32.07° 0.499432 m
32.98° 0.476832 m
[...]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The sensor used for this project produces a stream of about 1,500 such pairs every second and sends this data to the computer over a serial port.&lt;/p&gt;
&lt;h2 id=&quot;sensor-controlprocessing-program&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#sensor-controlprocessing-program&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Sensor Control/Processing Program&lt;/h2&gt;
&lt;p&gt;The sensor data is then passed to the first of two computer programs I wrote for this project. This first program is responsible for processing the sensor data and determining the position of each hand within each zone. If the sensor is rotating quickly enough, we can consider every measurement taken in one rotation as a single snapshot in time. Then, for each angle/distance pair, we can plot a point on a graph at the given angle and distance from the origin. In other words, we convert the pairs of &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mtext&gt;angle&lt;/mtext&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mtext&gt;distance&lt;/mtext&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(\text{angle},\text{distance})&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord&quot;&gt;angle&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord text&quot;&gt;&lt;span class=&quot;mord&quot;&gt;distance&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; coordinates to pairs of &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(x,y)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; coordinates and display the result.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;752&quot; height=&quot;620&quot; src=&quot;https://breq.dev/_astro/sensor_raw.BuLp--Cs_lhOGh.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Pictured above is the graph that is generated from this process. In this image, the green dots represent points found by the laser, and the purple and orange rings mark distances of 50 and 100 centimeters away from the sensor respectively. This scan was performed in my dorm room. This complete plot of the environment includes the performer’s body, the walls of the room, and any other objects in the environment at the sensor’s height. Next, it is necessary to filter out anything from the environment which should not affect the output, i.e., everything except the hands of the performer. I decided to accomplish this by processing two separate zones in the pot, such that the performer could move each of their hands within its respective zone in two dimensions. This results in four separate axes of control. The use of two separate zones was chosen to maximize the number of axes available to the user, and to eliminate the possibility of one hand obscuring the other.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;752&quot; height=&quot;620&quot; src=&quot;https://breq.dev/_astro/sensor.C_LVNfKV_ZRzXRQ.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The user can select these two zones by clicking each of the eight corners on the graph. In the chart above, my left hand is visible in the zone on the left side and my right hand is visible in the zone on the right side. The sensor, represented by the white dot in the center, is located in the middle of the playing surface, between the zones and directly in front of my body. The software will calculate the position of each point in the scene along each of the four axes. To do this, it constructs a projective transformation matrix, which is a technique from linear algebra. The following procedure was adapted from this &lt;a href=&quot;https://math.stackexchange.com/a/339033&quot;&gt;StackExchange answer&lt;/a&gt; by Dr. Martin von Gagern.&lt;/p&gt;
&lt;p&gt;The software knows the coordinates of each corner in space, and it knows the position of each corner relative to each axis (the point where the two axes meet is &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(0,0)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, the opposite corner is &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(1,1)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, etc). Using this, it will construct a transformation matrix that can map any point in space to its position relative to each axis. Intuitively, the most obvious approach would be to construct a &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;2\times2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; matrix to perform this mapping, since we are working with pairs of coordinates. However, since we have four separate known mappings, the matrix would have too many conditions to meet. The system would be overdetermined and thus impossible to solve.&lt;/p&gt;
&lt;p&gt;We can add “wiggle room” to the system by using a &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;3\times3&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; matrix instead. However, this requires representing our coordinates using triples instead of pairs. In other words, this requires some way of representing coordinates in the form &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;z&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(x,y,z)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.04398em;&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; instead of &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(x,y)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. One such approach uses homogenous coordinates. Mapping Cartesian coordinates to homogenous coordinates requires adding a 1 in the third position (such that &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(x,y)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; becomes &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(x,y,1)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;), and mapping homogenous coordinates back into Cartesian coordinates requires dividing the first and second coordinates by the third coordinate (such that &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;z&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(x,y,z)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.04398em;&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; becomes &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mfrac&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mi&gt;z&lt;/mi&gt;&lt;/mfrac&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mfrac&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mi&gt;z&lt;/mi&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(\frac x z, \frac y z )&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.095em;vertical-align:-0.345em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6954em;&quot;&gt;&lt;span style=&quot;top:-2.655em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.04398em;&quot;&gt;z&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.345em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7475em;&quot;&gt;&lt;span style=&quot;top:-2.655em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.04398em;&quot;&gt;z&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.4461em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.345em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;). Notably, this system creates a set of points with homogenous coordinates of the form &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(x,y,0)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; which cannot be mapped to Cartesian coordinates due to the division by zero. Conceptually, these points represent scenarios like the following:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;494&quot; height=&quot;181&quot; src=&quot;https://breq.dev/_astro/homogenous-coordinates.BI2j6bbn_Z1vopV3.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The above point is around &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;2.5&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;2.5&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;2.5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; on Axis 0, but it has no meaningful coordinate on Axis 1. As the positions of the performer’s hands should be inside the quadrilateral zone, scenarios like the above should never arise in this application. For each zone, the software will map the four corner points to their homogenous representation, then compute the &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;3\times3&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; transformation matrix. Then, it will sort the points based on whether the lie inside the zone. It determines if each point is inside the zone by checking if its position on either axis is less than zero or greater than one; although more optimized methods of testing if a point is inside a quadrilateral certainly exist, I found the matrix multiplication to be approach reasonably performant, so I decided against introducing additional complexity. Finally, to create a single value for each axis, the software will average the axis positions of each point located inside the zone. This step produces a stream of four numbers, representing the position of both hands along both axes in their respective zones:&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;[...]
0.43 0.53 0.21 0.54
0.41 0.57 0.19 0.50
0.42 0.61 0.19 0.52
[...]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This stream of values is then passed to the second program.&lt;/p&gt;
&lt;h2 id=&quot;midi-mapping-program&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#midi-mapping-program&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;MIDI Mapping Program&lt;/h2&gt;
&lt;p&gt;The second program handles mapping each axis to a stream of MIDI data. Each of the four axes are handled separately. The program provides a control panel which allows configuring the parameters of the MIDI stream.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;328&quot; height=&quot;900&quot; src=&quot;https://breq.dev/_astro/mapping.BhWjvE_o_Z9PMU3.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Each axis can be mapped to either note messages or Continuous Controller (CC) messages. For note messages, the range of notes is configurable. Additionally, pitch bend messages can be sent to smoothly interpolate between notes. As currently implemented, transitioning from one note to another smoothly requires that the sound source envelope not have attack/decay or this attack will be audible when moving between notes. For CC messages, the control number can be chosen. Both modes support setting the channel number, which is useful for having different axes control different synth voices. A setting to invert the signal is also provided.&lt;/p&gt;
&lt;h2 id=&quot;surface-configurations&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#surface-configurations&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Surface Configurations&lt;/h2&gt;
&lt;p&gt;Since the scanning occurs about 2 inches above the tabletop, the user does not need to touch any surface to use the controller. This allows any variety of surfaces to be placed underneath the setup. The simplest configuration has no reference material whatsoever. However, I found that this made it difficult to use, as I would frequently move my hand into or out of the zone without knowing, unexpectedly playing or stopping a tone.&lt;/p&gt;
&lt;p&gt;I also tried placing a piece of paper in each zone and calibrating the corners of the zone to the corners of the page. This solved the aforementioned issue. This configuration worked well for controlling a filter or some other aspect of the sound timbre, but I found that an 8.5x11” zone was too small to precisely control pitch. I also found that, without any sort of reference, it was almost impossible for me to precisely reach a specific note. At this time, I tried decreasing the range of the controller from 2 octaves to 1 octave in an attempt to improve precision, without much success. I also tried disabling the pitch bend messages to see if snapping the hand position to the nearest note would help, but I did not notice any improvement in usability.&lt;/p&gt;
&lt;p&gt;Next, I tried an asymmetrical layout, with a portrait-oriented paper on the left and two landscape-oriented pages arranged lengthwise on the right. The left page showed a 4x4 grid, and the right pages had vertical lines denoting the boundaries between notes. I found that this made me much more able to hit a specific note within the octave range, even if I re-enabled pitch bends. That said, I did not play along to any other instruments, so I might have overestimated my ability to precisely reach a particular pitch.&lt;/p&gt;
&lt;p&gt;I found it difficult not to hit intermediate notes. While moving my hand, even quickly, if a scan saw my hand in the middle of the move, it would play the intermediate note for about 150ms, which sounded jarring at times. To compensate, I played by lifting my hand up away from the zone before moving it side-to-side between notes. This created an additional issue: due to the shape of my hand, moving it up and down could alter the detected position along the forward-and-back axis. I tried playing using a credit card instead of my hand but found it just as easy to ensure my hand was not tilted forward or back when moving it up or down.&lt;/p&gt;
&lt;h2 id=&quot;mapping-configurations&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#mapping-configurations&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Mapping Configurations&lt;/h2&gt;
&lt;p&gt;The first mapping I tried used the controller to control a wavetable synthesizer in Ableton Live. It used the right hand horizontal axis to control pitch and the right hand vertical axis to control the position on the wavetable. The left hand controlled a low-pass filter, with the horizontal axis controlling the frequency cutoff and the vertical axis controlling the resonance parameter. I found wavetable synthesis to be a natural fit for this controller, since having a single parameter as the primary control of the sound’s timbre could give one hand control over both pitch and timbre, freeing up the other hand for controlling an effect or a second sound source.&lt;/p&gt;
&lt;p&gt;My attempt at controlling two synthesizer voices with this project used one hand for each voice, making use of my software’s ability to send MIDI note messages on different channels corresponding to the input axis. I again used the horizontal axis for pitch. One patch was a sawtooth wave bass, with the vertical axis controlling a low-pass filter, and the other patch was a square wave in a higher octave, with the vertical axis controlling a delay effect. I found controlling two separate voices to be overwhelming. This was mostly because I needed to visually look at each hand to position it properly, which was difficult as they were on opposite sides of the playing area. I also found the delay effect control to have little utility outside of “spooky sound effects.” I did enjoy “tuning” the two voices to an interval by ear (since I was not playing with a note reference at the time) and controlling an almost-exact-interval with my hands to slightly tune or detune it produced some interesting sounds, but this was made difficult by the latency issues (discussed in a later section).&lt;/p&gt;
&lt;p&gt;Returning to the wavetable and low-pass filter mapping, I tried an alternate playing style by placing a small object in the filter zone to hold those axes at a specific point. This allowed me to focus more on playing melodies with my right hand and less on holding my left in a specific stable position. However, it also removed the “free space” aspect of the controller for that zone, and it took away from the direct coupling of hand-to-sound, reintroducing that hesitance before manipulating a control. Ultimately, this playing style is a tradeoff, diverging from my original vision in pursuit of usability, and I believe having it as an option is a net benefit.&lt;/p&gt;
&lt;p&gt;Finally, I tried using the controller in conjunction with a traditional MIDI keyboard. I deactivated the right side zone, placed the keyboard in its place, and kept my left hand in the left side zone. I found that this combination worked surprisingly well: the traditional keyboard allowed me to play notes accurately and with minimal latency, while my left hand was free to alter the timbre of the sound. I found the LiDAR controller felt much more expressive than a simple mod wheel; even though it had similar precision, having a large physical representation of a parameter gave me a better sense of the possibilities it provided, and coupling my hand directly to its output made experimentation feel more direct and natural. While this configuration also does not follow my initial vision for a single, configurable, all-purpose MIDI controller, it succeeded in allowing for intuitive and inviting free-space control of timbre while falling back on a familiar interface for pitch.&lt;/p&gt;
&lt;p&gt;Across each of these configurations, I found that mapping two related parameters to a single hand fundamentally changed how I conceptualized the controls. I tried a few mappings of this sort, such as controlling the frequency and resonance of a filter, the time and feedback of a delay module, the amount and wet/dry mix of an overdrive effect, and the rate and feedback of a phaser. In each of these cases, I found I had a much better sense of the timbre possibilities offered by these controls. With a traditional controller, with single knobs mapped to single controls, I would typically move just one knob at a time and miss vast swaths of potential sounds. Having two parameters mapped to 2D space encourages moving diagonally or in a circle to modify both parameters at once. Even though traditional CC controllers allow mapping the same knob to several controls, it is not the typical use case.&lt;/p&gt;
&lt;p&gt;One of my favorite moments was finding a particular spot in the 2D zone that caused the filter to resonate particularly strongly or the phaser to fit the melody I was playing perfectly. Exploring these pockets gave me the sense that I was exploring something inherently two-dimensional, instead of just manipulating two parameters at once.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;h2 id=&quot;scan-latency&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#scan-latency&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Scan Latency&lt;/h2&gt;
&lt;p&gt;The issue of scan latency proved difficult to overcome. The LiDAR sensor that I use spins at about 380 RPM when connected to 5V power, leading to an effective latency of around 150 milliseconds between scans. This had a few adverse effects on the usability of the controller.&lt;/p&gt;
&lt;p&gt;The first issue with the scan latency was the creation of a 150ms “rhythm” in the audio output. The output would jump sharply to a new pitch whenever scan data was received, so if the user were to attempt a gradual slide between notes, the result would sound much choppier than intended. A potential remedy is to interpolate smoothly between scans, ramping to each value just as the measurement following it is received, but doing this would double the effective latency of the controller. This behavior could also perhaps be used for effect if a piece were specifically written around it, but the sensor used in this project does not provide an easy way to precisely control the speed of the scan motor, so synchronizing it to a track would be difficult. Given the scope of the project, I decided against trying to implement precise motor speed control, although this could be a direction for further experimentation.&lt;/p&gt;
&lt;p&gt;The latency also proved to be a barrier for precise control. I had assumed that 150ms, while by no means ideal, would be passable for syncing with the beat of slower music, but I overlooked the impact of latency on other aspects of performance. With a continuous instrument (such as a theremin, or playing a guitar with a slide), the performer will adjust in real-time using their ear until they are playing the correct pitch. With the LiDAR controller, the presence of latency disrupts this real-time adjustment, making it extremely difficult to play in tune. Removing the pitch bend messages and snapping each position to the nearest semitone helped, but it was still difficult to precisely position one’s hand in a specific 1/12 of the zone without any markings for guidance. Ultimately, a visual reference was required to accurately play pitches, and even then, the controller was difficult to use.&lt;/p&gt;
&lt;h2 id=&quot;future-directions&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#future-directions&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Future Directions&lt;/h2&gt;
&lt;p&gt;One direction I did not explore was integrating graphic scores into the project. While I tried a configuration with a general note reference on the surface, it would also be possible to use a reference designed for a specific piece of music. Such a reference would be, in a sense, musical notation for the piece. A potential difficulty with this is the lack of a “time” axis to draw on, which could make duration or progression difficult to express in this notation.&lt;/p&gt;
&lt;p&gt;Another potential direction is to use interpolation to reduce the jarring steps between scans. I intended to implement this, but the way in which I structured my software did not lend itself well to this approach, and a major restructuring would be needed. I am curious as to how adding glide between scans would affect the perceived latency of the controller, and how much it would improve the discontinuity between scans. Notably, different amounts of glide could be explored. For example, interpolating for the full 150ms would yield the smoothest output but the greatest latency, but perhaps a glide of 20ms could mitigate most of the jarring clicks/jumps in the audio with a minimal impact on latency. Additionally, different interpolation functions could be explored.&lt;/p&gt;
&lt;p&gt;More direct control over the sensor itself could be explored. The specific model of LiDAR sensor I used lacks an interface to control the motor speed or scan rate. A sensor with more features could allow tweaking these parameters, potentially leading to reduced latency and/or increased precision, especially since LiDAR sensors are typically calibrated to scan an entire room instead of a tabletop surface. The scan rate could even be tuned to match the tempo of the music being played, resulting in a much more predictable playing experience and eliminating the issue of sudden off-beat jumps in output.&lt;/p&gt;
&lt;p&gt;Finally, gathering the perspectives of others would be an important next step. Due to the timing of this project ending over spring break, I did not have much opportunity to share this project with others. Of course, when I tested the controller, I brought along my past intuitions and experiences. Other users would be drawing on different prior intuition when using the controller, and understanding its usability from more perspectives could inform how it could be made more intuitive and more inviting.&lt;/p&gt;&lt;/youtube&gt;</content:encoded></item><item><title>MOTD Necklace</title><link>https://breq.dev/projects/motd/</link><guid isPermaLink="true">https://breq.dev/projects/motd/</guid><description>Display a different message every day on an e-Ink necklace.</description><pubDate>Fri, 13 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;6904&quot; height=&quot;4608&quot; src=&quot;https://breq.dev/_astro/both.DLAowBmV_Z1iW0Pa.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;The necklace, in two separate frames. On the left, it shows my radio license in a heart-shaped frame, on the right, it shows a message my friend came up with in a square frame.&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;This project is a necklace/amulet which can display a message on an e-Ink display. The persistence of the e-Ink makes it so that the display, once updated, does not require a battery to hold the image.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I was inspired by a display pendant idea I found on &lt;a href=&quot;https://learn.adafruit.com/trinket-slash-gemma-space-invader-pendant?view=all&quot;&gt;Adafruit&lt;/a&gt; which used an ATTiny85 and an LED matrix display to show an animation. I wanted to experiment with a wearable project, but I didn’t want to have to remember to charge it or design it around a large battery.&lt;/p&gt;
&lt;p&gt;I was also looking for an excuse to use an e-Ink display for a project, due to their unique design considerations and low power requirements. I decided an e-Ink screen could be a good fit for this project.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The project consists of a monochrome &lt;a href=&quot;https://www.adafruit.com/product/4196&quot;&gt;e-Ink display&lt;/a&gt; mounted in a 3D printed frame. Pogo pins on a programming board I made are used to connect to the pins on the display breakout board.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3000&quot; height=&quot;4000&quot; src=&quot;https://breq.dev/_astro/board.B2rFIKb3_UtFCo.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;The programming board, featuring an M0 board and a row of pogo pins.&lt;/p&gt;
&lt;p&gt;I’ve printed two frames that I can switch between: a smaller, square one and a larger, heart-shaped one.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;Even though I didn’t put any special attention into weatherproofing, I’ve worn the necklace outdoors / in the rain / on the beach plenty and I haven’t had any issues. This is probably because there is no onboard power: even though there is no special ingress protection, the display is fine as long as it is dry when programmed. Also, the 3D printed frame seems to fit the display tightly enough that dust/dirt/sand exposure doesn’t pose much of an issue.&lt;/p&gt;
&lt;p&gt;The programming process ended up working pretty well, and it’s definitely something I could change every day if I had the motivation to. The pogo pins do require force to push the display against them, and slipping up at the wrong moment can cause the display to be refreshed improperly, causing issues. Some sort of latch to hold the display in place during programming could have remedied this.&lt;/p&gt;
&lt;p&gt;Overall, I’m happy with how this project turned out, and the necklace (with clever message) has become a common feature of my daily outfit.&lt;/p&gt;</content:encoded></item><item><title>Artificial Soundscapes</title><link>https://breq.dev/projects/soundscapes/</link><guid isPermaLink="true">https://breq.dev/projects/soundscapes/</guid><description>Sounds generated from historical weather data of different cities.</description><pubDate>Fri, 13 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1481&quot; height=&quot;1057&quot; src=&quot;https://breq.dev/_astro/soundscapes.CoaQ_K9g_Z14rVmi.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;A screenshot of the sonification project in Ableton Live, with data mapped to automation lanes and MIDI notes.&lt;/p&gt;
&lt;iframe width=&quot;100%&quot; height=&quot;300&quot; scrolling=&quot;no&quot; frameborder=&quot;no&quot; allow=&quot;autoplay; encrypted-media&quot; src=&quot;https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/soundcloud%253Atracks%253A1267210762&amp;#x26;color=%23ff5500&amp;#x26;auto_play=false&amp;#x26;hide_related=false&amp;#x26;show_comments=true&amp;#x26;show_user=true&amp;#x26;show_reposts=false&amp;#x26;show_teaser=true&amp;#x26;visual=true&quot;&gt;&lt;/iframe&gt;&lt;div style=&quot;font-size: 10px; color: #cccccc;line-break: anywhere;word-break: normal;overflow: hidden;white-space: nowrap;text-overflow: ellipsis; font-family: Interstate,Lucida Grande,Lucida Sans Unicode,Lucida Sans,Garuda,Verdana,Tahoma,sans-serif;font-weight: 100;&quot;&gt;&lt;a href=&quot;https://soundcloud.com/breq&quot; title=&quot;breq&quot; target=&quot;_blank&quot; style=&quot;color: #cccccc; text-decoration: none;&quot;&gt;breq&lt;/a&gt; · &lt;a href=&quot;https://soundcloud.com/breq/soundscapes&quot; title=&quot;Artificial Soundscapes&quot; target=&quot;_blank&quot; style=&quot;color: #cccccc; text-decoration: none;&quot;&gt;Artificial Soundscapes&lt;/a&gt;&lt;/div&gt;
&lt;p class=&quot;caption&quot;&gt;The final sonification, featuring sounds generated from Boston, Los Angeles, and Anchorage data.&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;In this project, I attempted to use data sonification techniques to create abstract soundscapes for various cities. Notably, the process of producing sounds from data is as similar as possible between the various cities, to allow the listener to compare and contrast these cities based on each soundscape. I wanted the listener to be able to understand the different aesthetic of each soundscape as a whole, but I also wanted to enable the listener to recognize how specific measurements and data points differed both over time and between cities.&lt;/p&gt;
&lt;p&gt;One challenge I had was balancing the aesthetics of the compositions with the need to convey information. Sonification typically uses more abstract sound sources, but these can sound jarring and disjointed. Alternatively, soundscapes often blend layers of sound together, but this using this approach could impede the actual presentation of data. As I wanted to create a piece which could serve both purposes, I needed to find a compromise between these opposing goals.&lt;/p&gt;
&lt;p&gt;To solve this, I decided to focus solely on facilitating comparing and contrasting of data. Thus, I could tweak the processing of data to achieve a more pleasant sound, and as long as I applied these tweaks equally to each city, it would not impact the listener’s ability to compare between cities. An example of this sort of tweak is mapping data to notes of the major scale instead of arbitrary pitches: this removes distracting dissonance and yields a more musical result, but it does further decouple the audio result from the data it is based on.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I drew some inspiration from soundscape compositions, which framed naturally occurring sounds in a more narrative way. Instead of sounds recorded from nature with a microphone, however, I worked with sounds generated from natural data. I focused on natural data partly as an homage to soundscape compositions. I only used data from the span of one year, but a future project could examine multiple decades to show a more dynamic view of the climate.&lt;/p&gt;
&lt;p&gt;One of the primary criticisms of sonification as a field is bias in the data processing: when creating an algorithm to convert data into sound, people are likely to have some preconceived notion of what the result should sound like. (For instance, sonification of space-themed data often uses whooshing noises, not because they aid in interpreting the data, but because science fiction has taught us that is what space should sound like.) The mapping from data to sound should leverage existing intuition if possible to help listeners understand the audio, but it should not detract from the data itself. These decisions have to be arbitrary, making this a difficult problem to solve. I drew heavily on Tantacrul’s &lt;a href=&quot;https://www.youtube.com/watch?v=Ocq3NeudsVk&quot;&gt;critique&lt;/a&gt; of sonification during this project to understand this balance.&lt;/p&gt;
&lt;p&gt;One of the most important facets of this project is that the same processing is applied to each city. My hope is that this reduces the likelihood that the processing step I have developed is biased towards telling a specific story or focusing on a particular theme. Without this restriction in place, I might allow my own understanding of cities to influence the results of the sonification instead of letting it be driven by the data.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;I sourced weather data for this project from NOAA’s free Climate Data Online service. This service gives daily temperature, wind, and precipitation data for a large number of stations both in the US and globally. Most US cities had multiple stations to choose from, and I specifically chose stations from the largest nearby airport (as these stations often report more data). I chose to analyze data over the course of one year, since it was a short enough interval for individual days to be represented as notes while being long enough to demonstrate the periodic nature of the seasons. I looped this data to emphasize this repetition. I also sourced tidal data from NOAA’s Tides and Currents service.&lt;/p&gt;
&lt;p&gt;While I wanted to try to incorporate additional sources (such as public transit data, air traffic data, or highway traffic data) into the soundscape, I could not find a practical way to accomplish this. Although many cities have transit APIs, these lack consistency, and they generally only provide data about the current location of trains and the predicted future schedule (whereas this project is more focused on historical data). Most air traffic APIs are similarly focused on present and future data only, and their rate limits would not allow me to retrieve a year’s worth of data at once for this project. Finally, highway traffic is generally not precisely measured on a real-time basis.&lt;/p&gt;
&lt;p&gt;To process this data, I first used a Jupyter notebook and Python code. This program would read the data from each source, then map each data point to one or more MIDI messages. These messages were organized first by city, then by data source, then by “tick” / instant in time. Messages could be grouped together to occur at the same instant, and a list of “cleanup” messages was kept ensuring no notes were left playing if the audio was stopped early. I arbitrarily chose that a tick would represent 1/20 of a second, as it was fast enough for the data to sound continuous but slow enough for the ear to pick out individual data points.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;CITIES &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;LAX&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ANC&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;BOS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@dataclass&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Tick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    messages&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;mido&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Message&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    cleanup&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;mido&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Message&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

TRACKS&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Tick&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    city&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;weather&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;tides&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; city &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; CITIES
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Additional Python code could then play back these tracks simultaneously over different MIDI channels. An Ableton Live set was configured to receive MIDI input, send separate channels to separate tracks with separate instruments, and map MIDI CC messages to instrument parameters to allow them to be controlled by the incoming data. Finally, the resulting audio was recorded using Ableton Live. I chose this workflow because I had familiarity with these tools, having used Ableton Live for production and Jupyter notebooks for analysis (albeit not simultaneously). Although I experimented with using Max/MSP for a potentially cleaner and more extensible implementation, the timeline for the project made a familiar workflow more pragmatic.&lt;/p&gt;
&lt;p&gt;I decided to select cities with different climates in order to highlight their differences. I started with Boston, as I have familiarity with its climate. I considered New York, but I decided against it due to its proximity to Boston and similar weather. Next, I chose Los Angeles due to its famously stable climate, followed by Anchorage for its unique geographic location.&lt;/p&gt;
&lt;p&gt;While creating the sonification, I noticed that it was difficult to effectively use pitch to represent multiple measurements. Generally, pitch is an appealing quantity to map to, since it has such a wide range and is easy for the ear to recognize. However, mapping multiple quantities to different pitches simultaneously raised some problems. When multiple pitches are being played simultaneously, the listener can end up focusing on the interval instead of each separate note. This detracts from the interpretation of data since whether the interval is major or minor typically does not represent anything. This issue can be remedied somewhat by using distinct sound sources or by playing in separate registers, but I still decided to only map one measurement to pitch.&lt;/p&gt;
&lt;p&gt;Another tradeoff I made related to the continuous nature of the phenomena being sonified and the discrete nature of the measurements taken. I found that directly mapping measurements to synthesizer controls could produce a result which sounded discontinuous and disjointed, and I considered implementing some form of interpolation to make the audio result sound smoother. Ultimately, I decided against this, as it could potentially lead to a misleading presentation of the data. This decision, however, did make the audio result less pleasant to listen to, and it compromised on the soundscape aesthetic which I was trying to achieve.&lt;/p&gt;
&lt;p&gt;After making these tradeoffs, I eventually decided that trying to create a meaningful and useful sonification which was also an artistic piece following the aesthetic of a soundscape would not be a feasible endeavor. &lt;a href=&quot;https://sonification.de/son/definition/&quot;&gt;The aim of sonification is to present data in a scientific sense for the ear to recognize new patterns, not to create a piece of music.&lt;/a&gt; Massaging the data and representation to fit a specific predetermined aesthetic is fundamentally at odds with this goal. To be true to the sonification of data, I decided to focus less on the soundscape aspect of the project.&lt;/p&gt;
&lt;p&gt;This also highlighted a flaw in my earlier assumption that the comparative nature of this project could help eliminate bias. Although the sonification would not be biased based on one specific city’s impact on popular culture, it could still be biased based on my preconceptions about what aesthetic cities should have, the cultural significance of the weather, and other factors unrelated to the data itself. Additionally, through chasing a particular aesthetic, the resulting sonification could lose information, making it less effective at triggering new insights.&lt;/p&gt;
&lt;p&gt;I decided to use two sound sources: a wavetable synthesizer to represent the weather data, and white noise to represent the tidal data. The pitch of the wavetable was controlled by the maximum temperature on a given day. I used the Ableton “Basic Shapes” preset and mapped the wavetable parameter to the wind speed such that calm days were represented by sine or triangle waves and windy days were represented by sawtooth waves. Finally, I used a low-pass filter on the white noise controlled by the water level to represent the tides.&lt;/p&gt;
&lt;p&gt;I used linear equations to map specific data values to notes or parameter values, attempting to show as much of the usable range as possible. However, while tuning these equations, I focused most on the Boston data. When I tried these equations with the Anchorage data, I found that the temperature dipped below the valid range of note values and the tide data was typically beyond the possible extremes of the control. The tide data was still understandable, but the temperature graph was inaudible during winter.&lt;/p&gt;
&lt;p&gt;With a visual graph, I would simply make the graph taller to include all of the relevant information. With sonification, however, I have to work within a limited range of notes. I considered adjusting the mapping to raise all notes up, but that could have caused issues with representing the Los Angeles summer (or any warmer cities, for that matter). I also considered making the mapping denser to fit both low and high temperatures, but that would have made the Los Angeles data harder to understand (as there was little variation from winter to summer anyway, and I wanted to not flatten this any further). I also could have shifted just the Anchorage data up. While that could have helped the listener understand more about the climate of Anchorage, it would have interfered with their ability to compare the data with the other cities. Eventually, I decided to leave the equations in place, allowing some of the Anchorage data to be missing from the audio result.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;Overall, the sonification I produced has some utility, although it did not accomplish the goals I started with. While developing the data processing pipeline, I found that I needed to let go of my expectations for the aesthetic of the result if I wanted the data to be presented in a meaningful way. I also found it more difficult than expected to develop a mapping from data to sound which leveraged the existing intuition of the listener without being biased towards a particular narrative and overshadowing the data. Listening to the end result, I can certainly hear and recognize specific differences between the cities: the stable climate of Los Angeles, the harsh winds of Boston, and the extreme tides of Anchorage.&lt;/p&gt;</content:encoded></item><item><title>ATtiny85 Stacker Game</title><link>https://breq.dev/projects/tiny-stacker/</link><guid isPermaLink="true">https://breq.dev/projects/tiny-stacker/</guid><description>A simple stacking game running on a low-power Atmel chip.</description><pubDate>Fri, 13 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;iframe src=&quot;https://www.youtube.com/embed/dYO6Px-RuYo?si=s_ofJ9itNmFts5d6&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; class=&quot;w-full aspect-video rounded-2xl overflow-clip&quot;&gt;&lt;/iframe&gt;
&lt;p class=&quot;caption&quot;&gt;A quick demonstration of the game.&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;This is a standalone game device. The game is a simple stacking game, requiring the user to press the button at the specific time to align each layer. The button is implemented by sensing skin conductivity.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I wanted to try my hand at a low-power embedded project. In the past, I had done projects which ran off of battery power, but I wanted to try to make something that could be left with the batteries in for years and still function. This required working with the sleep mode on my microcontroller of choice, which was something I hadn’t used before.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The microcontroller is an &lt;a href=&quot;https://www.sparkfun.com/products/9378&quot;&gt;ATtiny85&lt;/a&gt;, and it’s connected to an &lt;a href=&quot;https://www.adafruit.com/product/1052&quot;&gt;I2C LED matrix&lt;/a&gt; with an HT16K33 chip.&lt;/p&gt;
&lt;p&gt;The techniques for putting the microcontroller in sleep mode are based on this &lt;a href=&quot;https://learn.adafruit.com/trinket-slash-gemma-space-invader-pendant/source-code&quot;&gt;tutorial&lt;/a&gt; from Adafruit.&lt;/p&gt;
&lt;p&gt;The code uses functions from &lt;code&gt;&amp;#x3C;avr/sleep.h&gt;&lt;/code&gt; and &lt;code&gt;&amp;#x3C;avr/power.h&gt;&lt;/code&gt;, documented on the avr-libc site: &lt;a href=&quot;https://www.nongnu.org/avr-libc/user-manual/group__avr__sleep.html&quot;&gt;sleep.h&lt;/a&gt;, &lt;a href=&quot;https://www.nongnu.org/avr-libc/user-manual/group__avr__power.html&quot;&gt;power.h&lt;/a&gt;. (Documentation for the &lt;code&gt;set_sleep_mode&lt;/code&gt; macro is notably absent, but the source is &lt;a href=&quot;https://www.nongnu.org/avr-libc/user-manual/sleep_8h_source.html&quot;&gt;here&lt;/a&gt;.) The Wandering Engineer also published a more detailed &lt;a href=&quot;https://thewanderingengineer.com/2014/08/11/pin-change-interrupts-on-attiny85/&quot;&gt;description&lt;/a&gt; of the registers used (&lt;code&gt;PCMSK&lt;/code&gt; and &lt;code&gt;GIMSK&lt;/code&gt;). The &lt;code&gt;MCUCR&lt;/code&gt; (MCU Control Register) is documented &lt;a href=&quot;https://web.ics.purdue.edu/~jricha14/Interrupts/MCUCR.htm&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here’s the meat of the implementation:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot; data-language=&quot;cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Disable unused peripherals (Timer1 and ADC)&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;power_timer1_disable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;power_adc_disable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Enable interrupt on PB4 pin change&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// (Set the Pin Change Interrupt 4 bit in the Pin Change Mask register)&lt;/span&gt;
  PCMSK &lt;span class=&quot;token operator&quot;&gt;|=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_BV&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;PCINT4&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;awaitButton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Send command 0x20 to the I2C display&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// (system setup / turn off system oscillator / standby mode)&lt;/span&gt;
  TinyWireM&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;beginTransmission&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x70&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  TinyWireM&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  TinyWireM&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endTransmission&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Enable Pin Change Interrupts&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// (Set the Pin Change Interrupt Enable bit in the General Interrupt Mask register)&lt;/span&gt;
  GIMSK &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_BV&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;PCIE&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;


  &lt;span class=&quot;token comment&quot;&gt;// Disable all modules&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;power_all_disable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Enter power down mode&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Set the sleep mode 1 bit (SM1) in the MCU control register (MCUCR)&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;set_sleep_mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;SLEEP_MODE_PWR_DOWN&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Enable sleep mode&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Set the sleep enable bit (SE) in the MCU control register (MCUCR)&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;sleep_enable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Enable interrupts globally&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// (Set the global interrupt flag, I, in the status register, SREG)&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;sei&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Enter sleep mode&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;sleep_mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Clear the General Interrupt Mask register&lt;/span&gt;
  GIMSK &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Enable Timer 0 and the USI (Serial) peripherals&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;power_timer0_enable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;power_usi_enable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Re-initialize the display&lt;/span&gt;
  TinyWireM&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;initDisplay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;I’ve left it with the batteries in for more than a year by now, and I haven’t noticed a drop in the display brightness. I’d consider it a success, although the rationale behind some of the hardware decisions has definitely been lost to time… Why didn’t I just use an actual button? Regardless, this project certainly helped me get closer to understanding AVRs at a low level.&lt;/p&gt;</content:encoded></item><item><title>Wordle Clones</title><link>https://breq.dev/projects/wordle/</link><guid isPermaLink="true">https://breq.dev/projects/wordle/</guid><description>Two clones of Wordle, written in TypeScript and Rust.</description><pubDate>Fri, 13 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;425&quot; height=&quot;310&quot; src=&quot;https://breq.dev/_astro/typescript.Cl0evSYA_23FIV.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Wordle&quot;&gt;Wordle&lt;/a&gt; is a popular word guessing game, kind of like Mastermind but for letters. I’ve written a few clones of the game in different languages.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;The TypeScript clone was born out of a desire to understand the letter coloring procedure. I had played Wordle before, and I wanted to see if I could implement it myself.&lt;/p&gt;
&lt;p&gt;The Rust clone was written because I wanted to learn more about Rust. I figured Wordle was a complex enough game that implementing it cleanly would require a decent understanding of Rust features and best practices.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;h2 id=&quot;typescript&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#typescript&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;TypeScript&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;309&quot; height=&quot;423&quot; src=&quot;https://breq.dev/_astro/typescript_complete.BSKbJUAa_1Qu9ch.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Code is available at &lt;a href=&quot;https://github.com/breqdev/wordle&quot;&gt;breqdev/wordle&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I built this project in React, but I wanted to ensure the game logic was sufficiently decoupled from the rendered result. I wrote this logic in two pure TypeScript functions, &lt;code&gt;rowColoring&lt;/code&gt; and &lt;code&gt;keyboardColoring&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The row coloring function assigns colors to each letter in the word. I took advantage of the type system to define letters in the target as explicitly nullable, allowing them to be “removed” when matched by a letter in the guess.&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot; data-language=&quot;typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rowColoring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guess&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; target&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Store the color alongside each guess letter&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; guessLetters&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; LetterGuess&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; guess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;letter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    letter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    color&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;gray&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Store the target in an array of nullables&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; targetLetters&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// First pass: match green letters&lt;/span&gt;
  guessLetters &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; guessLetters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; letter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; color &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// green letters are matched by the specific index in the target&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;letter &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; targetLetters&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// remove matching green letters from the pool&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// so that they aren&apos;t also matched as yellows&lt;/span&gt;
      targetLetters&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; letter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; color&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;green&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; letter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; color &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Second pass: greedily match yellow letters&lt;/span&gt;
  guessLetters &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; guessLetters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; letter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; color &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;green&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// don&apos;t modify existing green letters&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; letter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; color &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// yellow letters are matched by searching the entire target word&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;gray&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt; targetLetters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;letter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// remove yellow letters once matched,&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// each letter only matches once&lt;/span&gt;
      targetLetters&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;targetLetters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;letter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; letter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; color&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yellow&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; letter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; color &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; guessLetters&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Wordle, the keyboard serves an important role: it shows how much information you have gotten about a letter based on your guesses. Dark gray signifies that the letter does not appear (it was not colored in a previous attempt), yellow signifies that it does appear (it was colored yellow in a previous attempt), and green signifies that you have correctly guessed the position at least once (it was colored green in a previous attempt). As the coloring of each letter relies on the coloring of previous attempts, the keyboard coloring function makes use of the row coloring function to color each of the guesses.&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot; data-language=&quot;typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;keyboardColoring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guesses&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; target&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; letters&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Record&lt;span class=&quot;token operator&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; LetterGuess&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; letter &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;abcdefghijklmnopqrstuvwxyz&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    letters&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;letter&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; letter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; color&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;gray&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; guess &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; guesses&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; coloring &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rowColoring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guess&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; letter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; color &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; coloring&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;letters&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;letter&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;gray&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt; color &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;gray&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        letters&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;letter&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;black&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;letters&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;letter&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;gray&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; color &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;green&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        letters&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;letter&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; color&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; letters&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;rust&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#rust&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Rust&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Code is available at &lt;a href=&quot;https://github.com/breqdev/rust_wordle&quot;&gt;breqdev/rust_wordle&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;697&quot; height=&quot;553&quot; src=&quot;https://breq.dev/_astro/rust.BmjcDLiE_Z1KaN0d.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I wanted to rely on as many zero-cost abstractions as possible. For storing each row and each word, instead of &lt;code&gt;Vec&lt;/code&gt;s allocated on the heap, I decided to use fixed-length arrays with type aliases:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; data-language=&quot;rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Word&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Square&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  color&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  letter&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Row&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Square&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I used a Trait to implement printing the row:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; data-language=&quot;rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;PrintWordle&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;print_wordle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PrintWordle&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Row&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;print_wordle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; square &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;iter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; boxed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;│ &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to_owned&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      boxed&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push_str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;square&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;letter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to_string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      boxed&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push_str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; │&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token function&quot;&gt;print_colored&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;square&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;boxed&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token macro property&quot;&gt;print!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;  &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  row&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;print_wordle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I tried to make use of a functional style for the scoring algorithm, relying on iterators for most of the heavy lifting:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; data-language=&quot;rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;score_guess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Word&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; guess&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Word&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Row&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Map each letter of the target to an Option, so we can &quot;remove&quot; it later&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; remaining &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;c&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// All tiles start off white&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; guess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;letter&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Square&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    color&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;White&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    letter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Use `.enumerate()` to check for the right tile in the right index&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; square&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;iter_mut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; target&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; guess&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      square&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Green&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      remaining&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Greedily take remaining unmatched target letters to turn guess letters yellow&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; square&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;iter_mut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; square&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;White&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pos&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; remaining&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;iter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;c&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; c &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guess&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        square&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Yellow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        remaining&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;pos&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Any unmatched squares become gray&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; square &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;iter_mut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; square&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;White&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      square&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Gray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  result
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I tried to keep this all straightforward, but I still wasn’t too confident that I had nailed all of the edge cases. I was delighted by Rust’s testing support:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot; data-language=&quot;rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;mod&lt;/span&gt; &lt;span class=&quot;token module-declaration namespace&quot;&gt;tests&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;expect_score&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; guess&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; colors&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token attribute attr-name&quot;&gt;#[test]&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;correct_guess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;expect_score&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ARRAY&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ARRAY&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;vec!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Green&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using &lt;code&gt;cargo&lt;/code&gt; was also a welcome relief from &lt;a href=&quot;https://breq.dev/2021/08/29/pockey/#the-rp2040-sdk&quot;&gt;fighting with C++ and git submodules&lt;/a&gt;. I used &lt;code&gt;rand&lt;/code&gt; to pick a random target word, &lt;code&gt;colored&lt;/code&gt; to print colored squares to the terminal, and &lt;code&gt;serde_json&lt;/code&gt; to read the wordlist files.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;The TypeScript implementation works well, and it’s actually my preferred Wordle to use due to its simple design, infinite puzzles, and the fact that it lets me keep playing after 6 wrong guesses. In hindsight, some memoization could have improved the performance of my declarative approach, as recoloring every row on every render undoubtedly has a performance penalty. That said, it would have been a tradeoff, and I don’t think it’s necessary given the relatively small number of guesses being used.&lt;/p&gt;
&lt;p&gt;The Rust implementation is undoubtedly a bit less usable, being a CLI app, but I learned a lot about using constructs within Rust. While TypeScript had given me some intuition for how type aliases and type inference work, and C++ had given me a basic understanding of stack and heap memory, concepts such as Traits and the borrow checker were completely new to me. This wasn’t a huge project, but the variety of data structures and paradigms it involved gave me a decent birds-eye view of Rust as a language.&lt;/p&gt;</content:encoded></item><item><title>Nuisance</title><link>https://breq.dev/projects/nuisance/</link><guid isPermaLink="true">https://breq.dev/projects/nuisance/</guid><description>Northeastern University Information, Services, and Notifications Considered Essential</description><pubDate>Thu, 24 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;902&quot; height=&quot;854&quot; src=&quot;https://breq.dev/_astro/nuisance.CVcj77Mg_Z28TLYB.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;The Nuisance dashboard, in light mode.&lt;/p&gt;
&lt;p&gt;Nuisance is a dashboard I made collecting links to Northeastern University student portals and online services.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;Northeastern has a few different collections of links: myNortheastern, which was disorganized and shut down, and the Student Hub, which is, frankly, filled with irrelevant information and loading spinners. I wanted an unobtrusive portal that would load quickly and link to the services I found most useful.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The page is built using React and Create-React-App, and styled with Tailwind. It doesn’t have any notable features other than a dark mode option and a setting to choose between opening links in the current tab or a new tab. Both of these settings are persisted in &lt;code&gt;localStorage&lt;/code&gt;. In hindsight, React was almost certainly not necessary, but I had experience with a component-based project structure and wanted to iterate quickly.&lt;/p&gt;
&lt;p&gt;I picked Cloudflare Pages for hosting, to make sure the site loaded fast.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;This was a lot more successful than I had thought it would be. I shared it with a few friends, and it’s accumulated a fair number of users across Northeastern. In hindsight, it isn’t that surprising to think that since I had a problem, others probably did too.&lt;/p&gt;
&lt;p&gt;I didn’t collect a ton of user feedback: I added GitHub links for suggestions, but only a month or so after I initially shared it. This might have led to broader adoption. They didn’t take much effort to add, so in future projects, I’ll make an effort to add them initially instead of waiting for popularity to come.&lt;/p&gt;
&lt;p&gt;I also didn’t have any sort of analytics configured (since I had meant for this project to just be for myself), which made it hard to measure how much traffic the dashboard was getting. In hindsight, I guess it doesn’t really matter what the numbers say, though. This project made my day-to-day life a bit more smooth, and it helped out a few friends, too.&lt;/p&gt;</content:encoded></item><item><title>BotBuilder</title><link>https://breq.dev/projects/botbuilder/</link><guid isPermaLink="true">https://breq.dev/projects/botbuilder/</guid><description>A build-your-own Discord bot service.</description><pubDate>Wed, 23 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;864&quot; height=&quot;573&quot; src=&quot;https://breq.dev/_astro/botbuilder.CGY2KtVW_ZUWBFS.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;A couple demo commands built using Blockly.&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;BotBuilder is an online tool that allows people to build custom Discord commands by dragging blocks. These commands are then added as slash commands to the user’s guild ).&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;After building the &lt;a href=&quot;https://breq.dev/projects/flask-discord-interactions&quot;&gt;flask-discord-interactions&lt;/a&gt; library, I realized how Discord’s Interactions API could enable interesting custom commands with less overhead than a traditional Gateway-based bot. Many of my friends wanted to create their own Discord bot to include custom commands, but running a Discord bot typically requires registering as a developer, finding hosting, handling tokens, installing a library, writing code, and other tasks that might prove difficult for someone inexperienced with programming. I wanted to create a service that would allow users to add custom commands to their Discord servers without any prior knowledge of bot development or code.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;Users log in with their Discord account using OAuth2 to access their workspace. There, they are able to use &lt;a href=&quot;https://developers.google.com/blockly&quot;&gt;Blockly&lt;/a&gt; to create commands.&lt;/p&gt;
&lt;p&gt;When users edit their workspace, the workspace is converted to XML and uploaded to the BotBuilder server, and if they close and reopen the workspace, their previous workspace is automatically downloaded and restored. At each upload, each command is also compiled into JavaScript, and each JavaScript function is uploaded to the BotBuilder server.&lt;/p&gt;
&lt;p&gt;Initially, users must click the “Add To Server” button, which will direct them through Discord’s OAuth2 flow to authorize BotBuilder to add commands to their guild. After this, when new commands are uploaded, the BotBuilder server calls the Discord API to update the slash commands present in each of the user’s guilds, adding any new commands and removing any deleted ones. It also maintains which set of users push commands to which guilds in a Redis database.&lt;/p&gt;
&lt;p&gt;When a command is executed in one of these Discord servers, Discord will send a POST request to BotBuilder. After verifying the cryptographic signature of this request, BotBuilder will look up which users added commands to the guild, and look through these commands for one that matches the incoming request.&lt;/p&gt;
&lt;p&gt;After identifying the command, BotBuilder will execute the JavaScript code and return the result as a response to the POST request. To execute JavaScript, I’m using &lt;a href=&quot;https://github.com/sqreen/PyMiniRacer/&quot;&gt;PyMiniRacer&lt;/a&gt;, a tool developed by &lt;a href=&quot;https://www.sqreen.com/&quot;&gt;Sqreen&lt;/a&gt; to run and interact with Google’s &lt;a href=&quot;https://v8.dev/&quot;&gt;V8&lt;/a&gt; JavaScript engine from Python.&lt;/p&gt;
&lt;p&gt;I chose to use JavaScript as the compilation target for the commands for security reasons. Executing, say, Python code in a sandbox would be difficult to do securely. JavaScript, on the other hand, is designed to be run in the browser, which is an inherently sandboxed environment.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;Not many of my friends ended up using this. I think the main reason was beacuse not enough features were present, and most people who wanted to make a custom command would need more functionality. Some of this was a limitation of Discord—without a bot user, the Interactions API is much more limited, and it doesn’t allow applications to look up user information, modify roles, or do other actions that most bots typically do. However, supporting additional features like custom command arguments and HTTP request blocks would have maybe turned this into a more useful service.&lt;/p&gt;</content:encoded></item><item><title>Cards</title><link>https://breq.dev/projects/cards/</link><guid isPermaLink="true">https://breq.dev/projects/cards/</guid><description>Generate and embed digital cards featuring custom images and text.</description><pubDate>Wed, 23 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;iframe class=&quot;block mx-auto my-8 rounded-2xl mt-4&quot; height=&quot;300&quot; width=&quot;500&quot; src=&quot;https://cards.api.breq.dev/card/219156620534663947.html&quot;&gt;&lt;/iframe&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;This is a service to generate custom “cards” based on a defined template and user-supplied fields.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;While working on &lt;a href=&quot;https://breq.dev/projects/breqbot&quot;&gt;Breqbot&lt;/a&gt;, I wanted to replicate the “rank card” idea provided by bots like &lt;a href=&quot;https://mee6.xyz/&quot;&gt;MEE6&lt;/a&gt;, but with user-supplied information and images instead.&lt;/p&gt;
&lt;p&gt;I started by writing a program using PIL that would take in a user’s name, bio, and profile image, and generate a basic PNG. I was frustrated by the process and the end result. I had to manually implement things I had taken for granted in the world of web-dev, such as text wrapping and emoji support. The process of implementing and modifying the card templates was time-consuming and tedious. Additionally, when I tried to include these rudimentary images on Breqbot’s website, I needed to redo the entire layout in HTML and CSS.&lt;/p&gt;
&lt;p&gt;I had the idea of creating a standalone service to generate these cards based on a predefined template and output them to either an IFrame or an image file. The resulting output could be used anywhere: sent as a Discord message, included in a GitHub README, or embedded in a website.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The service will render an HTML template with the user-provided parameters. Then, if an image file is requested, it will use &lt;a href=&quot;https://github.com/pyppeteer/pyppeteer&quot;&gt;pyppeteer&lt;/a&gt; to take a screenshot of the HTML template using Chrome.&lt;/p&gt;
&lt;p&gt;It’s also possible to “freeze” a card, preserving its screenshot on the server and returning a permanent link/URL to the card. This avoids having to use pyppeteer for every request for the card. To generate the card IDs, I’m using another service I made, &lt;a href=&quot;https://breq.dev/projects/snowflake&quot;&gt;Snowflake&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;I wasn’t originally a huge fan of using a headless browser in the server-side, as it seemed like it would be a waste of resources and using the wrong tool for the job, but the service ended up working pretty well, although the time-to-first-byte is, predictably, pretty poor compared to the other projects I’ve made.&lt;/p&gt;
&lt;p&gt;Performance aside, the process of developing new cards is much easier now. At the time of writing, I’ve pretty much only dipped my toes into web development, but I was able to make a few templates pretty quickly that looked much better than the old PIL tool ones.&lt;/p&gt;
&lt;p&gt;While integrating this service with Breqbot and analyzing how it could be used, I noticed that most use cases will generate a card once and then embed it repeatedly. For instance, users will request each others’ cards on Breqbot more often than they will update their own, and cards put in e.g. GitHub profiles are typically created once and left as-is for a while. As such, it’s kind of wasteful to regenerate the card for every request, so I implemented the “Freezing” functionality. This was a cool experience: deploying a project, seeing how it was used, and then adding functionality where it was lacking.&lt;/p&gt;</content:encoded></item><item><title>Flask Discord Interactions</title><link>https://breq.dev/projects/flask-discord-interactions/</link><guid isPermaLink="true">https://breq.dev/projects/flask-discord-interactions/</guid><description>A Python library for making HTTP-based Discord bots with Flask.</description><pubDate>Wed, 23 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1015&quot; height=&quot;613&quot; src=&quot;https://breq.dev/_astro/breqbot-lite.BzQAZLuf_Z3LhTO.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;Some commands for Breqbot Lite, a bot I made with this library.&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;Recently, Discord introduced a new Slash Commands feature that allows bots to integrate using webhooks. This is a library that handles registering the commands, receiving interactions, sending responses, sending followup messages, and including message components like clickable buttons in your message. It’s written as a Flask extension, so you can add other pages to the app and handle scaling/serving like any other Flask app.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;Most Discord bots and libraries use a Bot user to connect to the Discord API. Bot users interact with Discord in a similar way to actual Discord users: they connect over a WebSocket and then send and receive events such as messages. This approach works well for basic bots, but it makes it difficult to scale. Alternatively, webhook-based bots can be deployed behind a load balancer and scaled up or down as needed without worrying about overloading the websocket or allocating different servers to different processes.&lt;/p&gt;
&lt;p&gt;That said, the webhook approach is significantly more limited. Webhook bots can’t manage channels, reactions, direct messages, roles, or most of the other features in Discord. However, for basic bots that don’t need these features, webhook bots can be easier to develop and deploy.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The library is designed to be similar to the popular &lt;a href=&quot;https://github.com/Rapptz/discord.py&quot;&gt;Discord.py&lt;/a&gt; library. It’s probably better to show than to tell:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; os
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; flask &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Flask
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; flask_discord_interactions &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; DiscordInteractions

app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Flask&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__name__&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
discord &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; DiscordInteractions&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DISCORD_CLIENT_ID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environ&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DISCORD_CLIENT_ID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DISCORD_PUBLIC_KEY&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environ&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DISCORD_PUBLIC_KEY&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DISCORD_CLIENT_SECRET&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environ&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DISCORD_CLIENT_SECRET&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;


&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@discord&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;Respond with a friendly &apos;pong&apos;!&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Pong!&quot;&lt;/span&gt;


discord&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;set_route&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/interactions&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
discord&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;update_slash_commands&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guild_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;environ&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;TESTING_GUILD&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; __name__ &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;__main__&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;run&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;discord.command()&lt;/code&gt; decorator creates a &lt;code&gt;SlashCommand&lt;/code&gt; and adds it to the application, and the &lt;code&gt;discord.set_route()&lt;/code&gt; function adds an HTTP route that handles interaction events. The library will automatically register the commands and remove old ones on launch. When it receives an interaction from Discord, it will verify the signature, parse the command options, run the command, and return the result.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;This was one of the first OAuth2 projects I made, which was cool. It works well enough for my basic testing bot. Overall, I’m pretty proud of this one: I saw a gap where a library didn’t exist, and I developed something to fill it.&lt;/p&gt;
&lt;p&gt;I’m also really glad to see that a small community has sprung up around this library. I listed it in the official Discord Developer Documentation, among the numerous other Slash Command libraries. So far, four other people have contributed code to the project through pull requests, and 22 issues have been filed in the issue tracker. It’s been an interesting experience to receive a bug report or feature request from a community member and then figure out how to prioritize it and how best to patch it.&lt;/p&gt;</content:encoded></item><item><title>Mini-ITX Computer Case</title><link>https://breq.dev/projects/itx-case/</link><guid isPermaLink="true">https://breq.dev/projects/itx-case/</guid><description>My custom-built PC case that fit in my backpack.</description><pubDate>Wed, 23 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3024&quot; height=&quot;2456&quot; src=&quot;https://breq.dev/_astro/photo.D444Idzk_Z9vjRv.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;
The finished product had about the same footprint as my laptop.
&lt;/p&gt;
&lt;p&gt;In late 2018/early 2019, I decided to build a desktop computer for use doing 3D rendering and experimenting with machine learning.&lt;/p&gt;
&lt;p&gt;Unfortunately, I didn’t document my work nearly as well as I should have, so this writeup is going to be a bit heavy on the renders and light on the photos.&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;This was a custom-built PC case that houses a mini-ITX motherboard, SFX power supply, and small-form-factor GPU. It was built out of a mix of wooden parts and 3D-printed parts that I designed.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;Around this time, I had started taking 3D rendering at MSSM, and I was disappointed with the poor rendering performance of my laptop. I decided that I wanted to build a more powerful computer that would better handle tasks like 3D modeling, rendering, and deep learning. In order to get the best sustained performance at my price point, I decided to go with a desktop instead of a higher-end laptop or a NUC.&lt;/p&gt;
&lt;p&gt;However, I wasn’t a big fan of how large most desktop computers were. Most PC cases seemed extremely large, which wasn’t convenient for me at all, since I had to go back and forth to MSSM every month at the time. While form factors like ITX (motherboards) and SFX (power supplies) had started to take off, even cases built for them seemed excessively large.&lt;/p&gt;
&lt;p&gt;I decided to build my own case, with a goal of making something no larger than a few vertically-stacked binders. I wanted it to be able to fit in my backpack or suitcase to make transportation easy.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The case consists of three wooden panels (the top, bottom, and front) that all screw into the 3D-printed interior structure. Because my 3D printer has only 120mm of build plate space, the interior is split into 6 separate sections which are held together by their mutual attachment to the wooden panels.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1436&quot; height=&quot;1144&quot; src=&quot;https://breq.dev/_astro/exploded.C1JF5bWY_Z13LsOk.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The case contains two distinct airflow zones—one for the GPU and one for the CPU. The GPU slots into the left side of the case, connecting to the motherboard using a riser. The PSU sits directly underneath it. Additionyally, there is space for an SSD alongside the fan.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1570&quot; height=&quot;734&quot; src=&quot;https://breq.dev/_astro/rightside.DizRSgaK_Z2vBe10.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;On the left side, a hard drive sits underneath the motherboard, and there is space for an additional drawer for storing USB cables or other small parts. Above these, the motherboard sits on standoffs, and an SSD can attach to the front. This area has its own fan which blows air mostly above the motherboard, but some goes towards the hard drive as well.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1552&quot; height=&quot;729&quot; src=&quot;https://breq.dev/_astro/leftside.DqNrpaNn_Z2qpdNe.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the far corner of the render, you can see a small keyhole shape. This is a place where power button, status LED, or other types of modules can be inserted. I only ever made one such module, a simple on/off switch.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;I used this case for over a year, and it held up pretty well. By the end of its life, the power button had broken, and the screws were starting to strip inside the 3D-printed plastic. However, structurally, it held up well, even after getting roughly tossed into a suitcase many times.&lt;/p&gt;
&lt;p&gt;After I came back home from MSSM, I transferred my rig back over to an off-the-shelf case, since I had plenty of space for it. That said, I certainly wouldn’t call the project a failure—it was a great case for the 2 years or so that I used it, and I learned a lot about 3D printing in the process. Specifically, this project made me really consider the structural qualities of a 3D print, and I had to choose the orientation of each piece carefully in order to ensure each one was strong. While I used up plenty of filament in the process, by the end, this was something that I’m really proud of!&lt;/p&gt;</content:encoded></item><item><title>3D Printer Light Tower</title><link>https://breq.dev/projects/light-tower/</link><guid isPermaLink="true">https://breq.dev/projects/light-tower/</guid><description>An indicator to represent 3D printer status.</description><pubDate>Wed, 23 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1720&quot; height=&quot;1290&quot; src=&quot;https://breq.dev/_astro/light-tower.sMwqUsH-_Z1I58se.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;NeoPixels never look good on camera.&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;This is a small indicator light strip that I mounted next to my 3D printer. It indicates if the printer is ready, printing, or in an error state.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;In June 2021, I started a summer job in the Texas Instruments semiconductor fab in South Portland. There were a lot of different machines in the fab, but they all had one standardized light tower design. This helped me learn how to operate them more quickly, transfer my knowledge between different areas, and understand the status of a machine at a glance.&lt;/p&gt;
&lt;p&gt;I noticed that the indicators shown for a fab machine (load, unload, error) were similar to those I might want to show for a 3D printer. So, I set about building my own “light tower” to attach to my 3D printer.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;I used Adafruit’s NeoPixel library to write a Python script that changes the status displayed on the light tower. However, this script needs to run as root, since Adafruit’s library uses DMA (direct memory access) to control the Pi’s PWM module.&lt;/p&gt;
&lt;p&gt;I then wrote an Octoprint plugin that handles printer events. I used the &lt;a href=&quot;https://docs.octoprint.org/en/master/plugins/mixins.html#eventhandlerplugin&quot;&gt;EventHandlerPlugin mixin&lt;/a&gt; to write a basic Python script that called the NeoPixel script when necessary.&lt;/p&gt;
&lt;p&gt;Here is when an issue arose: Octoprint runs with a user account, but the NeoPixel script needs to run as root. I needed some way to allow the Octoprint user account to execute a program as the root user.&lt;/p&gt;
&lt;p&gt;I decided to use the Unix “setuid” system to allow the Octoprint user to invoke the NeoPixel script with the permissions of the root user. Since setuid can’t be used for scripts, I wrote a wrapper function to pass along the arguments and run the NeoPixel script as root.&lt;/p&gt;
&lt;pre class=&quot;language-c&quot; data-language=&quot;c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x3C;string.h&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x3C;stdio.h&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x3C;stdlib.h&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x3C;sys/types.h&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;#x3C;unistd.h&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; argc&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; argv&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setuid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    argv&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./tower.py&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;execv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./tower.py&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; argv&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this in place, the chain of Octoprint -&gt; Plugin -&gt; Setuid Wrapper -&gt; NeoPixel Script -&gt; NeoPixels worked perfectly.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;Generally, none of my projects have required an understanding of Linux permissions and libc functions like &lt;code&gt;setuid&lt;/code&gt; and &lt;code&gt;exec&lt;/code&gt;. A lot of my knowledge in this area was built up by watching &lt;a href=&quot;https://www.youtube.com/channel/UClcE-kVhqyiHCcjYwcpfj9w&quot;&gt;LiveOverflow&lt;/a&gt;. It was really fun to finally actually put some of this knowledge to use, and writing a &lt;code&gt;setuid&lt;/code&gt; program myself helped me better understand some of the security issues involved and the other specifics of this system.&lt;/p&gt;</content:encoded></item><item><title>Links</title><link>https://breq.dev/projects/links/</link><guid isPermaLink="true">https://breq.dev/projects/links/</guid><description>A dynamic URL shortener made using SSR.</description><pubDate>Wed, 23 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;558&quot; height=&quot;461&quot; src=&quot;https://breq.dev/_astro/signin.tRkK7W7O_pYQA0.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;The sign-in screen.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;797&quot; height=&quot;563&quot; src=&quot;https://breq.dev/_astro/dashboard.BfzxyQmj_Znliuu.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;The dashboard for the URL shortener.&lt;/p&gt;
&lt;p&gt;Exactly what it says on the tin: a basic URL shortening service that allows changing the destination of the URL after creating it.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I had done some server-side-rendering work with Flask before, but I hadn’t ever approached it from the JavaScript side of things. I wanted to understand some of the frameworks that are used to perform SSR using Node.&lt;/p&gt;
&lt;p&gt;At the time I made this, the college application season wasn’t that far behind me, and I remember seeing mail from some colleges using a plus sign from their domain as a URL shortener, such as &lt;code&gt;https://wpi.edu/+FJI3DE&lt;/code&gt;. This seemed like a cool idea, since it wouldn’t interfere with existing routes but would provide short URLs on a recognizable and trusted domain.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;URLs are stored in &lt;a href=&quot;https://redis.io/&quot;&gt;Redis&lt;/a&gt;, since a key-value store seemed like a great fit for this scenario. Routing is handled using &lt;a href=&quot;https://koajs.com/&quot;&gt;koa&lt;/a&gt;. For requests to the redirect URLs, this is all that happens.&lt;/p&gt;
&lt;p&gt;For the login and dashboard pages, I used templates written in &lt;a href=&quot;https://mozilla.github.io/nunjucks/&quot;&gt;nunjucks&lt;/a&gt;, a JavaScript templating language similar to Python’s &lt;a href=&quot;https://www.palletsprojects.com/p/jinja/&quot;&gt;jinja2&lt;/a&gt; (used with &lt;a href=&quot;https://flask.palletsprojects.com/en/2.0.x/&quot;&gt;Flask&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Authentication was handled with JWTs as cookies, and I used the GitHub OAuth API instead of storing usernames and passwords. I didn’t use a client-side JavaScript framework, just plain old HTML forms and a bit of vanilla JS to handle some show/hide buttons.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;This project made me shift my thinking a lot. In many regards, I’d been spoiled by front-end frameworks. Working without one, I had to think about how I could use the platform to accomplish my goals. This in turn gave me a better understanding of concepts like JWTs, cookies, and HTML forms.&lt;/p&gt;
&lt;p&gt;It was cool to use platform features like forms and cookies to handle data submission and authentication without client side JS. Contrast this with &lt;a href=&quot;https://breq.dev/projects/flowspace&quot;&gt;flowspace&lt;/a&gt;, in which I just passed the token as an &lt;code&gt;Authorization&lt;/code&gt; header for every &lt;code&gt;fetch&lt;/code&gt; POST request I made. This was a pretty simple project, but I’m glad I took it on.&lt;/p&gt;</content:encoded></item><item><title>MakerGamer</title><link>https://breq.dev/projects/makergamer/</link><guid isPermaLink="true">https://breq.dev/projects/makergamer/</guid><description>A &quot;fantasy console&quot; built for tinkering.</description><pubDate>Wed, 23 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;530&quot; height=&quot;942&quot; src=&quot;https://breq.dev/_astro/makergamer.CpJMcQTF_rFMbq.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;
MakerGamer running on a PocketCHIP
&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;MakerGamer was a virtual video game console designed to make game development as accessible as possible and to encourage the sharing of games. It could play games written in Scratch, Python, and JavaScript, and it provided basic tools to modify the games people downloaded. I wrote it with the intent of running it on Next Thing Co.’s &lt;a href=&quot;https://www.theverge.com/circuitbreaker/2016/7/19/12227806/pocketchip-review-portable-linux-computer&quot;&gt;PocketCHIP&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;This project was inspired by Pico-8, a “fantasy console” that allowed people to write retro-feeling games for modern hardware. However, the learning curve for using Pico-8 was high, and I thought it would be cool to create something in the style of Pico-8 but that would be suitable for people learning to code for the first time. I set out to create a similar “fantasy console” that could play games written in Scratch but felt like a traditional video game console.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;I designed and programmed an interface for downloading Scratch projects by the project ID, adding them to a virtual library, and playing them on demand, using &lt;a href=&quot;https://phosphorus.github.io/&quot;&gt;Phosphorous&lt;/a&gt; to run then without Adobe Flash. Python games written using PyGame were executed with parameters set to make PyGame full screen. Web-based games were downloaded and opened in a web browser. The user interface (main menu, project editors, etc) and the code editor were written in Python using PyGame.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;It worked well enough for me to demo it to people at the STEM fair. But it didn’t really have that much actual use, as all the games could be played in a web browser anyway. I eventually scrapped plans to finish the project-editing functionality, as it wasn’t practical to write a code editor, image editor, and etc. that only ran on a tiny screen anyway.&lt;/p&gt;</content:encoded></item><item><title>Picto</title><link>https://breq.dev/projects/picto/</link><guid isPermaLink="true">https://breq.dev/projects/picto/</guid><description>A Pictochat clone built on Web technologies.</description><pubDate>Wed, 23 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;643&quot; height=&quot;943&quot; src=&quot;https://breq.dev/_astro/picto.BQqdYyZ8_Z1B7nb7.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;This is a clone of Pictochat built on top of Web technologies.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;I wish we never met&lt;br&gt; &lt;strong&gt;We broke up on PictoChat, crying on my DS&lt;/strong&gt;&lt;br&gt; I went to a birthday party for one of her friends&lt;br&gt; And now that this is over I can hate them, I don’t have to pretend&lt;/p&gt;
&lt;p&gt;- Glitch Gum, “NEVER MET!”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I grew up with a Nintendo DS, so it’s no surprise I have a ton of nostalgia for Pictochat. And when a certain hyperpop song rekindled that nostalgia, I wanted to find a way to experience Pictochat again.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The project is mostly just a React single-page-application (although there’s a small WebSocket server component &lt;a href=&quot;https://github.com/breqdev/pictoserver/blob/main/index.js&quot;&gt;here&lt;/a&gt; to rebroadcast messages).&lt;/p&gt;
&lt;p&gt;I didn’t strip any assets from Pictochat itself, and I’m not much of a sound or icon designer, so I made do with what I could find. I picked similar sounds from &lt;a href=&quot;https://github.com/breqdev/pictoserver/blob/main/index.js&quot;&gt;material.io&lt;/a&gt; and icons from &lt;a href=&quot;https://fontawesome.com/&quot;&gt;Font Awesome&lt;/a&gt;. Some of them are a better match than others, but overall, they match up pretty well.&lt;/p&gt;
&lt;p&gt;I made an effort to have usable keyboard navigation. The original DS allowed using either the stylus or control pad for navigating the interface, so I wanted this project to have a similar experience.&lt;/p&gt;
&lt;p&gt;I also tweaked the onboarding flow a bit. In the original Pictochat, the name and theme color of the user was read from the DS system settings. Since I’m only cloning Pictochat itself, I instead prompted for these during onboarding. I also tweaked the chatroom mechanics somewhat. The original Pictochat used the Nintendo Low Latency Protocol to create chatrooms with nearby DS handhelds within range over 2.4GHz. While a similar system would be fun to implement on dedicated Linux boxes (&lt;a href=&quot;https://wiki.archlinux.org/title/ad-hoc_networking&quot;&gt;IBSS&lt;/a&gt; + &lt;a href=&quot;https://www.open-mesh.org/projects/batman-adv/wiki&quot;&gt;batman-adv&lt;/a&gt;, anyone?), I certanly couldn’t implement it in a Web browser, so chatrooms are global.&lt;/p&gt;
&lt;p&gt;One of my favorite aspects of Pictochat was the closed-ness of the rooms: even though you might be in a room with unfamiliar people (e.g. at an event), you could be certain it was a relatively small group that you could get to know. Having four global chatrooms seemed counter to this, so I insted opted to let chatroom names be any arbitrary string. My hope was that people would choose mostly-unique names, keeping the number of users per chatroom relatively low.&lt;/p&gt;
&lt;p&gt;Building this as a React app and styling it with Tailwind was mostly straightforward. It was a bit difficult to get the viewport string correct such that the window wouldn’t become narrower on mobile — for once, we don’t actually want the site to be responsive, since it has to exactly match the layout of the original Pictochat! Also, keeping the state of the canvas in the message compose box was tough, since the state of the image couldn’t be extracted from the canvas element itself. As a workaround, I passed a ref object &lt;em&gt;down&lt;/em&gt; to the canvas, which assigned it to a dispatch function. Then, the parent component could dispatch commands down into the child and request state to flow up from it. It’s ugly, but I can’t think of a better way of doing things: I can’t exactly change how the platform works, and mixing multiple data models never works well.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;It isn’t perfect, but it’s accurate enough that I was able to relive some of my childhood: typing a bunch of text and scribbling furiously with the pen tool to make a message completely black, copying and editing a message to write all over it, and dragging letters all over the page as decoration.&lt;/p&gt;
&lt;p&gt;The WebSocket connection had some reliability issues, and I think my reconnection logic might have been broken somehow. Other than that, I was pretty happy with how everything turned out. It didn’t blow up, but it helped me and my friends scratch that nostalgic itch, which was nice.&lt;/p&gt;</content:encoded></item><item><title>R2D2 Clone</title><link>https://breq.dev/projects/r2d2/</link><guid isPermaLink="true">https://breq.dev/projects/r2d2/</guid><description>Made of an old plastic bucket.</description><pubDate>Wed, 23 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;530&quot; height=&quot;942&quot; src=&quot;https://breq.dev/_astro/r2d2.BYB-x_lt_KUiiw.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;
If you&apos;re wondering, the chassis is an old Play-Doh bucket.
&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;Exactly what it says on the tin: a remote-controlled robot built to look like R2D2. As time went on, I did add some more advanced features, such as text-to-speech and video streaming.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;This was made for several occasions: the STEM fair, and Halloween later that year when I went trick-or-treating as Luke Skywalker.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;I started with the guts of an earlier robot I had built: two Lego NXT motors, connected to a Raspberry Pi through an H-Bridge motor driver. I spent a lot of time adding features to this robot: it could be controlled using a Wii remote, a smartphone app, or an interactive website. It had speech synthesis software and an internal speaker, a camera mounted to allow video streaming, and a few LEDs for blinkenlights.&lt;/p&gt;
&lt;p&gt;By the end of the project, I had switched to using an NXT brain to control the motors, connected over USB to the Raspberry Pi.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;It did its job and did it well. This project was one of the first significant software projects I worked on, and it taught me how to design and organize a program longer than just a few lines.&lt;/p&gt;</content:encoded></item><item><title>Rave Choker / Outshine</title><link>https://breq.dev/projects/outshine/</link><guid isPermaLink="true">https://breq.dev/projects/outshine/</guid><description>A NeoPixel driver project, a companion Android app, and a stylish LED choker.</description><pubDate>Sun, 13 Feb 2022 00:00:00 GMT</pubDate><content:encoded>&lt;iframe src=&quot;https://giphy.com/embed/Ztwx0SfEd2tgDE7J2w&quot; width=&quot;270&quot; height=&quot;480&quot; frameborder=&quot;0&quot; class=&quot;mx-auto my-4&quot;&gt;&lt;/iframe&gt;
&lt;p class=&quot;caption&quot;&gt;
  The latest revision of the choker, displaying a multi-color fade/wipe animation.
&lt;/p&gt;
&lt;iframe src=&quot;https://giphy.com/embed/WHmc9HI2lXWgnEgY7Y&quot; width=&quot;270&quot; height=&quot;480&quot; frameborder=&quot;0&quot; class=&quot;mx-auto my-4&quot;&gt;&lt;/iframe&gt;
&lt;p class=&quot;caption&quot;&gt;
  The first revision of the choker, displaying a simple back-and-forth animation.
&lt;/p&gt;
&lt;p&gt;This project consists of three parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/breqdev/outshine&quot;&gt;outshine&lt;/a&gt;, an Arduino program that displays animations on a NeoPixel strand&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/breqdev/OutshineApp&quot;&gt;OutshineApp&lt;/a&gt;, an Android app to communicate with an Arduino running Outshine via USB serial&lt;/li&gt;
&lt;li&gt;the rave choker, the physical device that I built which runs Outshine firmware&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;h2 id=&quot;outshine&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#outshine&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Outshine&lt;/h2&gt;
&lt;p&gt;Outshine itself was based on some work I did for NU Rover, Northeastern University’s team which competes in the Mars Society’s University Rover Challenge. On the rover, LEDs are controlled by an ATmega328PB separate from the primary STM32 microcontroller. As such, to implement the NeoPixel handling, I essentially had free reign over this chip.&lt;/p&gt;
&lt;p&gt;Functionally, the rover is required to display different colors to indicate its control mode (teleoperated or autonomous) and status about its autonomous navigation. It does not need to show animations, but I included them anyway because I figured they could be useful for displaying more detailed status information.&lt;/p&gt;
&lt;h2 id=&quot;rave-choker&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#rave-choker&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Rave Choker&lt;/h2&gt;
&lt;p&gt;I got a ticket to a &lt;a href=&quot;https://twitter.com/officialrezz&quot;&gt;Rezz&lt;/a&gt; show, and I wanted to incorporate some flashy LEDs in my outfit somehow. Rezz is known for her LED glasses which display a spiral pattern. I wanted to create something similarly colorful and animated, but not a direct reimplementation of the glasses.&lt;/p&gt;
&lt;p&gt;The process started with me standing in front of a mirror and holding a lit LED strip up to different parts of my body. Eventually, I settled on a choker, because I liked the look of it the best.&lt;/p&gt;
&lt;p&gt;Originally, I had intended for the choker to be controlled via Wi-Fi by broadcasting its own network and exposing a server with a web UI. I had some familiarity with this architecture, as it’s similar to how I built the &lt;a href=&quot;https://breq.dev/projects/wallmatrix&quot;&gt;wall matrix&lt;/a&gt; project.&lt;/p&gt;
&lt;p&gt;At the time, I had recently bought a limited-edition &lt;a href=&quot;https://www.adafruit.com/product/5299&quot;&gt;pink RP2040 Feather&lt;/a&gt; board from Adafruit, and I really wanted to use it for a project. This board doesn’t have Wi-Fi or Bluetooth, so I figured I would add an additional Pi Zero to handle the networking.&lt;/p&gt;
&lt;p&gt;With the added bulk of another board in mind, I decided to explore putting the boards and battery off of my neck and into a hat.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1148&quot; height=&quot;1148&quot; src=&quot;https://breq.dev/_astro/hat.B8GUQWn0_Z9bB3I.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;This worked to some extent, but the stretchiness of the hat didn’t pair well with the fragile ribbon of the LED strip. In the end, I ditched the idea as I thought it seemed too fragile for extended use.&lt;/p&gt;
&lt;h2 id=&quot;outshineapp&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#outshineapp&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;OutshineApp&lt;/h2&gt;
&lt;p&gt;Going back to the drawing board, I decided to try leaving the Feather board on my neck and directly attaching it to my phone over USB. The phone would interface with the board over serial and provide power to it (removing the need for a dedicated battery).&lt;/p&gt;
&lt;p&gt;I tried to use the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API&quot;&gt;WebSerial API&lt;/a&gt; for this, but was hit with a really stupid issue involving Chrome on Android:&lt;/p&gt;
&lt;tweet id=&quot;1476122496319606787&quot;&gt;
&lt;p&gt;As such, I switched to a native app. I built it in React Native, because I already had some familiarity with React.&lt;/p&gt;
&lt;h2 id=&quot;redesign&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#redesign&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Redesign&lt;/h2&gt;
&lt;p&gt;Tragically, this first revision of the choker was left slightly trampled after I went a little too hard in a &lt;a href=&quot;https://twitter.com/fo0dhouse&quot;&gt;food house&lt;/a&gt; moshpit. I was able to quickly repair the broken solder joints, and the LED strip was barely damaged (somehow only the red channel of the last pixel was broken), but I still realized that I needed to rethink the design.&lt;/p&gt;
&lt;p&gt;I had the following goals in mind for the redesign:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Remove the need for a tether between the phone and the choker (as this could get dangerous in a crowd, or yanking it could cause the choker to come off of me)&lt;/li&gt;
&lt;li&gt;Make the clasp at the back of the choker more sturdy&lt;/li&gt;
&lt;li&gt;Remove the silicone outer layer from the LEDs&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;h2 id=&quot;outshine-1&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#outshine-1&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Outshine&lt;/h2&gt;
&lt;p&gt;The Outshine firmware takes in commands over I2C, UART, or Bluetooth. Initially, commands were four bytes long:&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;[1 byte] Red channel
[1 byte] Green channel
[1 byte] Blue channel
[1 byte] Animation command
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Later, I reworked the protocol to use a two-byte indicator before each byte, to prevent the ends of the protocol from getting out of sync:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;01&lt;/code&gt;: Start Transaction, remaining 6 bits are the animation ID&lt;/li&gt;
&lt;li&gt;&lt;code&gt;10&lt;/code&gt;: Color data, next 6 bits are a portion of an RGB color
&lt;ul&gt;
&lt;li&gt;Color data is shifted in by this command, so any existing color data will be shifted left by 6 bits&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;11&lt;/code&gt;: Final color data, next 6 bits are color data and this is the end of an RGB color
&lt;ul&gt;
&lt;li&gt;This can be followed by either additional colors (&lt;code&gt;10&lt;/code&gt;) or the end of the transaction (&lt;code&gt;00&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;00&lt;/code&gt;: End Transaction&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I2C support was added for Rover, UART support was added for debugging the Rover but was later used for the rave choker, and Bluetooth support was added for the V2 choker.&lt;/p&gt;
&lt;p&gt;The driver relies on one of two libraries, depending on the architecture:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.adafruit.com/adafruit-neopixel-uberguide/arduino-library-installation&quot;&gt;Adafruit_NeoPixel&lt;/a&gt; for ATmega Arduino boards&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.adafruit.com/adafruit-neopxl8-featherwing-and-library/neopxl8-arduino-library&quot;&gt;Adafruit_NeoPXL8&lt;/a&gt; for the RP2040, to take advantage of the chip’s &lt;a href=&quot;https://hackspace.raspberrypi.com/articles/what-is-programmable-i-o-on-raspberry-pi-pico&quot;&gt;PIO&lt;/a&gt; capabilities&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The code makes use of a table of function pointers to control the active animation. The animation state (e.g. current active pixel in a wipe animation, current frame in a fade animation) is persisted across a shared struct to make transitions between animations more smooth.&lt;/p&gt;
&lt;h2 id=&quot;outshineapp-1&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#outshineapp-1&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;OutshineApp&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;792&quot; height=&quot;1760&quot; src=&quot;https://breq.dev/_astro/app.Bq0wMUjA_Z295Htf.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The app is built in React Native, using &lt;a href=&quot;https://github.com/melihyarikkaya/react-native-serialport&quot;&gt;react-native-serialport&lt;/a&gt; to handle the USB serial communication and &lt;a href=&quot;https://github.com/dotintent/react-native-ble-plx/&quot;&gt;react-native-ble-plx&lt;/a&gt; to handle Bluetooth. I’m just using off-the-shelf components for the color wheel, buttons, and brightness slider. I didn’t even bother to build a production version; I just keep the development &lt;code&gt;.apk&lt;/code&gt; on my phone.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1080&quot; height=&quot;2400&quot; src=&quot;https://breq.dev/_astro/app_v2.g9y1f8e1_kNgDv.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The V2 version of the app switched to simple color swatches and added support for multi-color configurations (complete with some pride-themed presets). This was much easier for me to manipulate quickly.&lt;/p&gt;
&lt;h2 id=&quot;rave-choker-1&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#rave-choker-1&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Rave Choker&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2347&quot; height=&quot;1760&quot; src=&quot;https://breq.dev/_astro/choker.Bz7WD4zR_2oisuN.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The V1 rave choker itself consisted of a strand of NeoPixels connected to an Adafruit &lt;a href=&quot;https://www.adafruit.com/product/5299&quot;&gt;Pink RP2040 Feather&lt;/a&gt;. I used a &lt;a href=&quot;https://en.wikipedia.org/wiki/Banana_connector&quot;&gt;“banana connector”&lt;/a&gt; as a clasp. It was wired to ground on both sides, so it wasn’t electrically functional.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2347&quot; height=&quot;1760&quot; src=&quot;https://breq.dev/_astro/board.DY4a_HeE_18422n.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The body of the choker was housed in a 3D-printed case, made from two parts that screw together using threaded inserts. The case has an open top to show off the pink RP2040, because I thought it looked cool.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3000&quot; height=&quot;4000&quot; src=&quot;https://breq.dev/_astro/old_choker_trans.lrJ3VA7C_285Nca.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The V2 rave choker also uses a 3D-printed frame, but it completely encloses the board. This frame also includes a battery, allowing for operation without a tether. I continued to use a Feather, but I opted for the &lt;a href=&quot;https://www.adafruit.com/product/4062&quot;&gt;nRF52840&lt;/a&gt; variant for Bluetooth support.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3000&quot; height=&quot;4000&quot; src=&quot;https://breq.dev/_astro/new_choker.CmmZrSq-_2uEtXS.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I used fake leather material and metal snaps to provide the structure of the choker, and I attached the NeoPixels to the material using E6000 adhesive, loosely following &lt;a href=&quot;https://learn.adafruit.com/neopixel-led-harness-bra&quot;&gt;this guide for a harness bra&lt;/a&gt; from Adafruit. I again blacked out the copper contacts on the front of the LED strip, but this time, I used black nail polish instead of eyeliner. The result vaguely resembles a studded look, and the choker doesn’t look out of place even with the lights turned off.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;3456&quot; height=&quot;4608&quot; src=&quot;https://breq.dev/_astro/new_choker_off.Dle1_wIO_Zi6WWU.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;It worked well at the Rezz show! In fact, a cute transfem noticed it, and we ended up talking and dancing together for most of the night :)&lt;/p&gt;
&lt;p&gt;I had initially been worried about the battery life being an issue, but as long as the LEDs aren’t solidly on full-white, it doesn’t seem to be a huge problem.&lt;/p&gt;
&lt;p&gt;I was also a bit worried about the “crowd-safety” of it—what if someone yanks on the cord?—but it came unplugged easily without yanking on my neck.&lt;/p&gt;
&lt;p&gt;I also wore the choker to &lt;a href=&quot;https://www.gaybashdboston.com/&quot;&gt;GAY BASH’D&lt;/a&gt;, a combination DJ set and drag show. I made a few revisions to the setup prior to this event, adding a brightness slider to OutshineApp and the 3D-printed case to the choker itself.&lt;/p&gt;
&lt;p&gt;After getting the Bluetooth version up and running, I also wore the choker to Carpenter Brut and SVDDEN DEATH shows, both of which had much more intense moshing than shows I had been to in the past. That said, the choker held up well! It seems to have some level of beer-spill-resistance, which is essential.&lt;/p&gt;
&lt;p&gt;Overall, I think I really achieved the goal I started with. The choker is something flashy, stylish, and uniquely “me,” and it stands out in the perfect way among a crowd at an EDM show. Also, the Outshine project will undoubtedly prove a useful starting point for future experimentation with NeoPixels.&lt;/p&gt;
&lt;p&gt;Starting off with a tethered solution wasn’t ideal (for instance, it made going through security a bit of a hassle). Even though the tether wasn’t too much of a hassle at more chill shows, it still was much more convenient to have a Bluetooth-based design.&lt;/p&gt;
&lt;p&gt;It’s also a bit cumbersome to change the animation, requiring me to unlock my phone and open the app. I only really wanted to change it every few songs, but it was still frustrating to have to stop raving to fiddle with a smartphone app. Physical controls would help remedy this, at the cost of more complexity. At the end of the day, I think the tradeoffs I made paid off.&lt;/p&gt;&lt;/tweet&gt;</content:encoded></item><item><title>WorkerSocket</title><link>https://breq.dev/projects/workersocket/</link><guid isPermaLink="true">https://breq.dev/projects/workersocket/</guid><description>A JavaScript library to run a WebSocket inside of a Web Worker.</description><pubDate>Wed, 09 Feb 2022 00:00:00 GMT</pubDate><content:encoded>&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; i workersocket
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;This is a library I made to run a WebSocket inside of a Web Worker in the browser. It exports a &lt;code&gt;WorkerSocket&lt;/code&gt; object which behaves as closely as possible to a browser WebSocket.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I wrote this while implementing my &lt;a href=&quot;https://github.com/breqdev/roslib/&quot;&gt;fork of roslib&lt;/a&gt;, a library for communicating with a &lt;a href=&quot;https://www.ros.org/&quot;&gt;&lt;code&gt;ros&lt;/code&gt;&lt;/a&gt; server through the &lt;a href=&quot;http://wiki.ros.org/rosbridge_suite&quot;&gt;&lt;code&gt;rosbridge&lt;/code&gt;&lt;/a&gt; protocol. &lt;code&gt;roslib&lt;/code&gt; is typically used to build a web-based monitoring UI for some type of robot.&lt;/p&gt;
&lt;p&gt;The original &lt;code&gt;roslib&lt;/code&gt; hijacked a browserify library called &lt;a href=&quot;https://github.com/browserify/webworkify&quot;&gt;&lt;code&gt;webworkify&lt;/code&gt;&lt;/a&gt; to run a WebSocket through a Web Worker, but &lt;code&gt;webworkify&lt;/code&gt; doesn’t bundle with Vite or Webpack 5.&lt;/p&gt;
&lt;p&gt;The reason &lt;code&gt;roslib&lt;/code&gt; defaults to putting a WebSocket inside of a Web Worker is to make sure that data is pulled away from the server as quickly as possible, even if it ends up building up in the web worker. Due to an oversight in the &lt;code&gt;rosbridge&lt;/code&gt; server, if a client can’t pull information quickly enough, then the server will use excessive resources caching data. This &lt;a href=&quot;https://github.com/RobotWebTools/roslibjs/pull/317&quot;&gt;pull request&lt;/a&gt; for the original &lt;code&gt;roslib&lt;/code&gt; describes the issue in more detail. Generally, the software on the robot is more important than the monitoring UI, so offloading the queueing to a background thread in the UI client makes sense.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The library consists of two parts: the web worker itself, and the &lt;code&gt;WorkerSocket&lt;/code&gt; implementation.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;WorkerSocket&lt;/code&gt; class mimics the API of a &lt;code&gt;WebSocket&lt;/code&gt;, but forwards messages to the web worker. The web worker then handles the actual socket. The &lt;code&gt;WorkerSocket&lt;/code&gt; class maintains an array of listeners for each event and calls each one as necessary. It also generates a unique ID for each &lt;code&gt;WorkerSocket&lt;/code&gt; class instantiated, which is then sent along with all messages to the web worker. The worker maintains a mapping from IDs to socket instances.&lt;/p&gt;
&lt;p&gt;The web worker is constructed using an object URL. This technique allows the worker to be bundled with any bundler without any special handling. It is somewhat unintuitive, though, using &lt;code&gt;Function.toString()&lt;/code&gt; to turn the worker implementation into a string, then placing it into an &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/IIFE&quot;&gt;immediately-invoked function expression&lt;/a&gt; using string manipulation. This is then turned into an object URL from which the worker is loaded.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; workerURL &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createObjectURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Blob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;(&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; workerImpl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;)();&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;text/javascript&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; worker &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Worker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;workerURL&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Testing is performed using Chai in the browser (and using Puppeteer to run headlessly).&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;It seems to work well. That said, there are so many edge cases with the behavior of WebSockets in the browser that I’m a bit hesitant to use it. I’ve written plenty of tests to cover common use cases, and I don’t know what I would add, but I also don’t feel like the test suite is entirely complete.&lt;/p&gt;
&lt;p&gt;I’ve published the result to &lt;code&gt;npm&lt;/code&gt; though, in case others want to make use of this.&lt;/p&gt;</content:encoded></item><item><title>Universal Hooks</title><link>https://breq.dev/2022/01/19/hooks/</link><guid isPermaLink="true">https://breq.dev/2022/01/19/hooks/</guid><description>Reimplementing some core React Hooks in userspace.</description><pubDate>Wed, 19 Jan 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you’ve studied discrete math, you might be familiar with the concept of a “universal gate.” You might have also heard that the NAND gate and NOR gate are each universal gates, since you can create any other gate by composing many NANDs or NORs.&lt;/p&gt;
&lt;p&gt;React Hooks compose just like logic gates. If you use multiple hooks, they shouldn’t interfere with each other, just as multiple logic gates in a circuit can operate independently. Any collection of Hooks can be abstracted away into a new custom hook, just as a collection of logic gates within a circuit can be abstracted into a single unit.&lt;/p&gt;
&lt;p&gt;While we’re not going to find a truly universal hook, we will show how a few basic hooks can be used to implement most of the others. We’re also not going to aim for complete API compatibility — each of our hooks will do its best to cover a few common use cases.&lt;/p&gt;
&lt;p&gt;This post is intended to be interesting reading for those with some experience with React, and to demonstrate some of the principles of hook composition. Thus, some familiarity with React is assumed.&lt;/p&gt;
&lt;h1 id=&quot;our-building-blocks&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#our-building-blocks&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Our building blocks&lt;/h1&gt;
&lt;p&gt;The &lt;a href=&quot;https://reactjs.org/docs/hooks-reference.html&quot;&gt;React docs&lt;/a&gt; list three “Basic Hooks.” Let’s step through and analyze what each of these do.&lt;/p&gt;
&lt;h2 id=&quot;usestate&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#usestate&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;useState&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;This hook will keep track of some value between renders. It also provides a function to set the value. Calling this function will trigger a re-render with the updated value.&lt;/p&gt;
&lt;h2 id=&quot;useeffect&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#useeffect&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;useEffect&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;This hook will run a function after the first render, before unmounting the component, and whenever one of the provided values changes.&lt;/p&gt;
&lt;h2 id=&quot;usecontext&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#usecontext&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;useContext&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;This hook uses the Context API to deeply pass values down the component tree. Since none of the other built-in hooks use the Context API, we won’t need to use this.&lt;/p&gt;
&lt;h1 id=&quot;implementations&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#implementations&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Implementations&lt;/h1&gt;
&lt;p&gt;I’ve taken the liberty to rearrange this list a bit from the docs, since we’ll use some of the earlier hooks to build some of the later ones.&lt;/p&gt;
&lt;h2 id=&quot;useref&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#useref&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;useRef&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;A Ref can be thought of as a “box” that holds a value. Refs are simply objects of the form:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows you to update the value of the ref, and any code with a reference to it will see the updated value. For instance, consider this code:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;makePrinter&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; printCount &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makePrinter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;printCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// outputs 0&lt;/span&gt;
count &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;printCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// outputs 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is admittedly quite contrived. However, in React, this comes up much more often. If you aren’t careful, your side effects, event callbacks, and other functions could close over an old value, and they won’t get updated when state changes.&lt;/p&gt;
&lt;p&gt;Let’s see how a ref can fix this code:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// or React.createRef(0);&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;makePrinter&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; printCount &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makePrinter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;printCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// outputs 0&lt;/span&gt;
count&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;printCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// outputs 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now’s a good time to point out the &lt;code&gt;const&lt;/code&gt;. In JavaScript, “constant” is not the same as “immutable.” The ref is the same object, it’s just mutated.&lt;/p&gt;
&lt;p&gt;With this in mind, let’s build our hook. We can use &lt;code&gt;useState&lt;/code&gt; to keep some state around. However, we don’t want to actually &lt;em&gt;set&lt;/em&gt; the state at all — refs don’t trigger renders. Our hook can take in some default value, create an object with &lt;code&gt;.current&lt;/code&gt;, and use &lt;code&gt;useState&lt;/code&gt; to persist it across renders.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;useRef&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;ref&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setRef&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; defaultValue &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; ref&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;useRef&lt;/code&gt; can also take in a function to create the default value on demand. Thankfully, &lt;code&gt;useState&lt;/code&gt; allows this too. We can always call &lt;code&gt;useState&lt;/code&gt; with a function argument, and in this function, we can test if the default value provided is itself a function, calling it if necessary.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;useRef&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;ref&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setRef&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; defaultValue &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;function&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;current&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; defaultValue &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; ref&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, let’s finish with an example:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot; data-language=&quot;jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;RefExample&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; messageRef &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; interval &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setInterval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;messageRef&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;backgroundColor &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;black&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        messageRef&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;backgroundColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;white&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        messageRef&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;backgroundColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;black&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;clearInterval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;interval&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;messageRef&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;messageRef&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Hello World!&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RefExample&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

ReactDOM&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;root&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;usereducer&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#usereducer&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;useReducer&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;This hook can be thought of primarily as an alternative to &lt;code&gt;useState&lt;/code&gt;. A reducer is a function that takes in a state and an action and returns an updated state.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; initial &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;reducer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;increment&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;decrement&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;reset&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Unsupported reducer action&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can use a reducer &lt;em&gt;without&lt;/em&gt; React as follows:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;initial &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// { count: 0 }&lt;/span&gt;

state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;increment&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// { count: 1 }&lt;/span&gt;

state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;increment&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;increment&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;decrement&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// { count: 2 }&lt;/span&gt;

state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;reset&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// { count: 0 }&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;reducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;double&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Error: Unsupported reducer action&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All right, let’s look back at React. Here’s how you might use &lt;code&gt;useReducer&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot; data-language=&quot;jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;ReducerExample&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dispatch&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useReducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reducer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; initial&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;count is &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;increment&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;decrement&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;decrement&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;reset&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;reset&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ReducerExample&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

ReactDOM&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;root&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;useReducer&lt;/code&gt; hook returns two values: &lt;code&gt;state&lt;/code&gt; and &lt;code&gt;dispatch&lt;/code&gt;. The &lt;code&gt;state&lt;/code&gt; object is, unsurprisingly, the current state. The &lt;code&gt;dispatch&lt;/code&gt; function is a function that calls the reducer with the current state to set the new state. Notably, you don’t have to provide the current state to &lt;code&gt;dispatch&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With this in mind, we can start scaffolding the signature of our version of this hook.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;useReducer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;reducer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; initial&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ll need to keep track of the current state somewhere. Let’s use the &lt;code&gt;useState&lt;/code&gt; hook for this. We’ll also need to define some kind of &lt;code&gt;dispatch&lt;/code&gt; function.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;useReducer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;reducer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; initial&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setState&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initial&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dispatch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// do something with the current state&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dispatch&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, remember that our reducer has signature &lt;code&gt;reducer(state, action)&lt;/code&gt;. In our dispatch function, we can call our reducer with the current state and provided action, then set the new state to the returned value.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;useReducer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;reducer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; initial&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setState&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initial&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dispatch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dispatch&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One more thing: React’s &lt;code&gt;useReducer&lt;/code&gt; allows passing in an initializer function as a third argument. We can match this behavior in our hook by passing a function to &lt;code&gt;setState&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;useReducer&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;reducer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; initial&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; initFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setState&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initFunction&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;initFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initial&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; initial&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dispatch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dispatch&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And use it like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;useReducer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reducer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; count &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;usememo&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#usememo&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;useMemo&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Memoization&lt;/em&gt; refers to the practice of making a function “remember” its past inputs and output, so that it doesn’t need to execute again if its inputs don’t change.&lt;/p&gt;
&lt;p&gt;Consider a function like:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;expensive&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;executing very expensive computation...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can make a new, memoized version that “remembers” its past inputs and output. To associate these inputs and outputs, we can use an immediately-invoked function expression.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; memoized &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; previousInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; previousOutput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;expensive&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;executing very expensive computation...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; previousInput&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; previousOutput&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      previousInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      previousOutput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;expensive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; previousOutput&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we try this, we can see that the function is only evaluated when the input changes:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Not memoized&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expensive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// executing very expensive computation..., 1&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expensive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// executing very expensive computation..., 1&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expensive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// executing very expensive computation..., 1&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expensive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// executing very expensive computation..., 7.3891&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Memoized&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;memoized&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// executing very expensive computation..., 1&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;memoized&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;memoized&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;memoized&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// executing very expensive computation..., 7.3891&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the building block we’ll need to use for our memoized function.&lt;/p&gt;
&lt;p&gt;React’s &lt;code&gt;useMemo&lt;/code&gt; is a bit different, though. It assumes that the function &lt;em&gt;closes&lt;/em&gt; over the values it needs, then uses an extra “dependency array” to determine when changes are necessary. Again, let’s start with its signature.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;useMemo&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;func&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; deps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;func&lt;/code&gt; is the function that we want to memoize, and &lt;code&gt;deps&lt;/code&gt; is the array of dependencies. This “works,” but doesn’t memoize anything.&lt;/p&gt;
&lt;p&gt;Since our hook will be called on each render, we’ll need a mechanism to keep the old value around. Remember, the whole point of this is to not re-invoke things on every render. We can &lt;code&gt;useRef&lt;/code&gt; to keep things around as a ref.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;useMemo&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;func&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; deps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; previousDeps &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;deps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; previousValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;func&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, &lt;code&gt;useRef(deps)&lt;/code&gt; returns a ref initialized to the dependency list. &lt;code&gt;useRef(func)&lt;/code&gt; takes advantage of the special behavior of &lt;code&gt;useRef&lt;/code&gt; when passed a function—it initializes the ref to the output of the function.&lt;/p&gt;
&lt;p&gt;Let’s compare the previous dependencies with the current dependencies. &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is&quot;&gt;&lt;code&gt;Object.is()&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every&quot;&gt;&lt;code&gt;Array.prototype.every()&lt;/code&gt;&lt;/a&gt; are both helpful here.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;useMemo&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;func&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; deps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; previousDeps &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;deps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; previousValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;func&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; matches &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; deps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;every&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;dep&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
    Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;is&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dep&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; previousDeps&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;matches&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    previousValue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; previousValue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s finish this one off with an example.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot; data-language=&quot;jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;MemoExample&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setX&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setY&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; exp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useMemo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Very expensive operation...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        x is &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;, y is &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;, exponent is &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;exp&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;increment x&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;y &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;increment y&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see &lt;code&gt;Very expensive operation...&lt;/code&gt; logged to the console whenever &lt;code&gt;x&lt;/code&gt; changes, but not when &lt;code&gt;y&lt;/code&gt; changes. &lt;code&gt;exp&lt;/code&gt; should always be kept in sync with &lt;code&gt;x&lt;/code&gt;, but the computation is only done when &lt;code&gt;x&lt;/code&gt; changes.&lt;/p&gt;
&lt;h2 id=&quot;usecallback&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#usecallback&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;useCallback&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;This hook is a variant of &lt;code&gt;useMemo&lt;/code&gt;, but it serves a different use case. &lt;code&gt;useMemo&lt;/code&gt; is primarily used to prevent extra computation. However, we can also use it to prevent the “identity” of a value from changing.&lt;/p&gt;
&lt;p&gt;Conceptually, the output of &lt;code&gt;useMemo&lt;/code&gt; changes only when the dependency array changes. This means, if the value is passed down to other components, those components will only re-render when the dependency array changes.&lt;/p&gt;
&lt;p&gt;Here’s an example (albeit a very contrived one) of this use:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot; data-language=&quot;jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;CallbackExample&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setX&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setY&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; squareX &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useMemo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;setX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        x is &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;, y is &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;, exponent is &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;exp&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;increment x&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;y &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;increment y&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ExpensiveComponent&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;squareX&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;squareX&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is mostly useful if the function goes into the dependency array of another hook somewhere else.&lt;/p&gt;
&lt;p&gt;In this case, there’s nothing expensive going on that needs memoizing—the function isn’t actually executed anyway. But syntax like &lt;code&gt;() =&gt; () =&gt; ...&lt;/code&gt; is difficult to read and understand. Thus, we can make a version of &lt;code&gt;useMemo&lt;/code&gt; that takes in a value directly, instead of a function that returns it.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;useCallback&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;callback&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; deps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useMemo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; callback&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; deps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;conclusion&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;In this post, we’ve walked through re-implementing a few React hooks by composing existing hooks and adding some logic. With just &lt;code&gt;React.useState&lt;/code&gt;, we’ve implemented &lt;code&gt;useRef&lt;/code&gt;, &lt;code&gt;useReducer&lt;/code&gt;, &lt;code&gt;useMemo&lt;/code&gt;, and &lt;code&gt;useCallback&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Is this practical? Not really. But I hope it gives a sense for what React hooks are “made of,” and gives a few examples on how hook composition can work.&lt;/p&gt;</content:encoded></item><item><title>remark-abcjs</title><link>https://breq.dev/projects/remark-abcjs/</link><guid isPermaLink="true">https://breq.dev/projects/remark-abcjs/</guid><description>A Remark plugin to format music notation.</description><pubDate>Tue, 04 Jan 2022 00:00:00 GMT</pubDate><content:encoded>&lt;div class=&quot;abcjsContainer&quot; style=&quot;overflow: hidden; height: 147.042px;&quot;&gt;&lt;svg xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; role=&quot;img&quot; fill=&quot;currentColor&quot; stroke=&quot;currentColor&quot; aria-label=&quot;Sheet Music for &amp;#x22;Nokia Tune&amp;#x22;&quot; width=&quot;770&quot; height=&quot;147.042&quot;&gt;&lt;title&gt;Sheet Music for &quot;Nokia Tune&quot;&lt;/title&gt;&lt;g&gt;&lt;text stroke=&quot;none&quot; font-size=&quot;27&quot; font-style=&quot;normal&quot; font-family=&quot;Times New Roman&quot; font-weight=&quot;normal&quot; text-decoration=&quot;none&quot; class=&quot;&quot; text-anchor=&quot;middle&quot; x=&quot;385&quot; y=&quot;49.56&quot; data-name=&quot;title&quot;&gt;&lt;tspan x=&quot;385&quot;&gt;Nokia Tune&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;g&gt;&lt;path d=&quot;M 15 89.07 L 755 89.07 L 755 89.77 L 15 89.77 z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot; class=&quot;abcjs-top-line&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 15 96.82 L 755 96.82 L 755 97.52 L 15 97.52 z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 15 104.57 L 755 104.57 L 755 105.27 L 15 105.27 z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 15 112.32 L 755 112.32 L 755 113.02 L 15 113.02 z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 15 120.07 L 755 120.07 L 755 120.77 L 15 120.77 z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;staff-extra clef&quot;&gt;&lt;path data-name=&quot;clefs.G&quot; d=&quot;M 29.689999999999998 75.257c 0.09 -0.09 0.24 -0.06 0.36 0c 0.12 0.09 0.57 0.6 0.96 1.11c 1.77 2.34 3.21 5.85 3.57 8.73c 0.21 1.56 0.03 3.27 -0.45 4.86c -0.69 2.31 -1.92 4.47 -4.23 7.44c -0.3 0.39 -0.57 0.72 -0.6 0.75c -0.03 0.06 0 0.15 0.18 0.78c 0.54 1.68 1.38 4.44 1.68 5.49l 0.09 0.42l 0.39 0c 1.47 0.09 2.76 0.51 3.96 1.29c 1.83 1.23 3.06 3.21 3.39 5.52c 0.09 0.45 0.12 1.29 0.06 1.74c -0.09 1.02 -0.33 1.83 -0.75 2.73c -0.84 1.71 -2.28 3.06 -4.02 3.72l -0.33 0.12l 0.03 1.26c 0 1.74 -0.06 3.63 -0.21 4.62c -0.45 3.06 -2.19 5.49 -4.47 6.21c -0.57 0.18 -0.9 0.21 -1.59 0.21c -0.69 0 -1.02 -0.03 -1.65 -0.21c -1.14 -0.27 -2.13 -0.84 -2.94 -1.65c -0.99 -0.99 -1.56 -2.16 -1.71 -3.54c -0.09 -0.81 0.06 -1.53 0.45 -2.13c 0.63 -0.99 1.83 -1.56 3 -1.53c 1.5 0.09 2.64 1.32 2.73 2.94c 0.06 1.47 -0.93 2.7 -2.37 2.97c -0.45 0.06 -0.84 0.03 -1.29 -0.09l -0.21 -0.09l 0.09 0.12c 0.39 0.54 0.78 0.93 1.32 1.26c 1.35 0.87 3.06 1.02 4.35 0.36c 1.44 -0.72 2.52 -2.28 2.97 -4.35c 0.15 -0.66 0.24 -1.5 0.3 -3.03c 0.03 -0.84 0.03 -2.94 0 -3c -0.03 0 -0.18 0 -0.36 0.03c -0.66 0.12 -0.99 0.12 -1.83 0.12c -1.05 0 -1.71 -0.06 -2.61 -0.3c -4.02 -0.99 -7.11 -4.35 -7.8 -8.46c -0.12 -0.66 -0.12 -0.99 -0.12 -1.83c 0 -0.84 0 -1.14 0.15 -1.92c 0.36 -2.28 1.41 -4.62 3.3 -7.29l 2.79 -3.6c 0.54 -0.66 0.96 -1.2 0.96 -1.23c 0 -0.03 -0.09 -0.33 -0.18 -0.69c -0.96 -3.21 -1.41 -5.28 -1.59 -7.68c -0.12 -1.38 -0.15 -3.09 -0.06 -3.96c 0.33 -2.67 1.38 -5.07 3.12 -7.08c 0.36 -0.42 0.99 -1.05 1.17 -1.14zm 2.01 4.71c -0.15 -0.3 -0.3 -0.54 -0.3 -0.54c -0.03 0 -0.18 0.09 -0.3 0.21c -2.4 1.74 -3.87 4.2 -4.26 7.11c -0.06 0.54 -0.06 1.41 -0.03 1.89c 0.09 1.29 0.48 3.12 1.08 5.22c 0.15 0.42 0.24 0.78 0.24 0.81c 0 0.03 0.84 -1.11 1.23 -1.68c 1.89 -2.73 2.88 -5.07 3.15 -7.53c 0.09 -0.57 0.12 -1.74 0.06 -2.37c -0.09 -1.23 -0.27 -1.92 -0.87 -3.12zm -2.94 20.7c -0.21 -0.72 -0.39 -1.32 -0.42 -1.32c 0 0 -1.2 1.47 -1.86 2.37c -2.79 3.63 -4.02 6.3 -4.35 9.3c -0.03 0.21 -0.03 0.69 -0.03 1.08c 0 0.69 0 0.75 0.06 1.11c 0.12 0.54 0.27 0.99 0.51 1.47c 0.69 1.38 1.83 2.55 3.42 3.42c 0.96 0.54 2.07 0.9 3.21 1.08c 0.78 0.12 2.04 0.12 2.94 -0.03c 0.51 -0.06 0.45 -0.03 0.42 -0.3c -0.24 -3.33 -0.72 -6.33 -1.62 -10.08c -0.09 -0.39 -0.18 -0.75 -0.18 -0.78c -0.03 -0.03 -0.42 0 -0.81 0.09c -0.9 0.18 -1.65 0.57 -2.22 1.14c -0.72 0.72 -1.08 1.65 -1.05 2.64c 0.06 0.96 0.48 1.83 1.23 2.58c 0.36 0.36 0.72 0.63 1.17 0.9c 0.33 0.18 0.36 0.21 0.42 0.33c 0.18 0.42 -0.18 0.9 -0.6 0.87c -0.18 -0.03 -0.84 -0.36 -1.26 -0.63c -0.78 -0.51 -1.38 -1.11 -1.86 -1.83c -1.77 -2.7 -0.99 -6.42 1.71 -8.19c 0.3 -0.21 0.81 -0.48 1.17 -0.63c 0.3 -0.09 1.02 -0.3 1.14 -0.3c 0.06 0 0.09 0 0.09 -0.03c 0.03 -0.03 -0.51 -1.92 -1.23 -4.26zm 3.78 7.41c -0.18 -0.03 -0.36 -0.06 -0.39 -0.06c -0.03 0 0 0.21 0.18 1.02c 0.75 3.18 1.26 6.3 1.5 9.09c 0.06 0.72 0 0.69 0.51 0.42c 0.78 -0.36 1.44 -0.96 1.98 -1.77c 1.08 -1.62 1.2 -3.69 0.3 -5.55c -0.81 -1.62 -2.31 -2.79 -4.08 -3.15z&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;staff-extra key-signature&quot;&gt;&lt;path data-name=&quot;accidentals.sharp&quot; d=&quot;M 54.781000000000006 78.227c 0.21 -0.12 0.54 -0.03 0.66 0.24c 0.06 0.12 0.06 0.21 0.06 2.31c 0 1.23 0 2.22 0.03 2.22c 0 0 0.27 -0.12 0.6 -0.24c 0.69 -0.27 0.78 -0.3 0.96 -0.15c 0.21 0.15 0.21 0.18 0.21 1.38c 0 1.02 0 1.11 -0.06 1.2c -0.03 0.06 -0.09 0.12 -0.12 0.15c -0.06 0.03 -0.42 0.21 -0.84 0.36l -0.75 0.33l -0.03 2.43c 0 1.32 0 2.43 0.03 2.43c 0 0 0.27 -0.12 0.6 -0.24c 0.69 -0.27 0.78 -0.3 0.96 -0.15c 0.21 0.15 0.21 0.18 0.21 1.38c 0 1.02 0 1.11 -0.06 1.2c -0.03 0.06 -0.09 0.12 -0.12 0.15c -0.06 0.03 -0.42 0.21 -0.84 0.36l -0.75 0.33l -0.03 2.52c 0 2.28 -0.03 2.55 -0.06 2.64c -0.21 0.36 -0.72 0.36 -0.93 0c -0.03 -0.09 -0.06 -0.33 -0.06 -2.43l 0 -2.31l -1.29 0.51l -1.26 0.51l 0 2.43c 0 2.58 0 2.52 -0.15 2.67c -0.06 0.09 -0.27 0.18 -0.36 0.18c -0.12 0 -0.33 -0.09 -0.39 -0.18c -0.15 -0.15 -0.15 -0.09 -0.15 -2.43c 0 -1.23 0 -2.22 -0.03 -2.22c 0 0 -0.27 0.12 -0.6 0.24c -0.69 0.27 -0.78 0.3 -0.96 0.15c -0.21 -0.15 -0.21 -0.18 -0.21 -1.38c 0 -1.02 0 -1.11 0.06 -1.2c 0.03 -0.06 0.09 -0.12 0.12 -0.15c 0.06 -0.03 0.42 -0.21 0.84 -0.36l 0.78 -0.33l 0 -2.43c 0 -1.32 0 -2.43 -0.03 -2.43c 0 0 -0.27 0.12 -0.6 0.24c -0.69 0.27 -0.78 0.3 -0.96 0.15c -0.21 -0.15 -0.21 -0.18 -0.21 -1.38c 0 -1.02 0 -1.11 0.06 -1.2c 0.03 -0.06 0.09 -0.12 0.12 -0.15c 0.06 -0.03 0.42 -0.21 0.84 -0.36l 0.78 -0.33l 0 -2.52c 0 -2.28 0.03 -2.55 0.06 -2.64c 0.21 -0.36 0.72 -0.36 0.93 0c 0.03 0.09 0.06 0.33 0.06 2.43l 0.03 2.31l 1.26 -0.51l 1.26 -0.51l 0 -2.43c 0 -2.28 0 -2.43 0.06 -2.55c 0.06 -0.12 0.12 -0.18 0.27 -0.24zm -0.33 10.65l 0 -2.43l -1.29 0.51l -1.26 0.51l 0 2.46l 0 2.43l 0.09 -0.03c 0.06 -0.03 0.63 -0.27 1.29 -0.51l 1.17 -0.48l 0 -2.46z&quot;&gt;&lt;/path&gt;&lt;path data-name=&quot;accidentals.sharp&quot; d=&quot;M 65.031 89.852c 0.21 -0.12 0.54 -0.03 0.66 0.24c 0.06 0.12 0.06 0.21 0.06 2.31c 0 1.23 0 2.22 0.03 2.22c 0 0 0.27 -0.12 0.6 -0.24c 0.69 -0.27 0.78 -0.3 0.96 -0.15c 0.21 0.15 0.21 0.18 0.21 1.38c 0 1.02 0 1.11 -0.06 1.2c -0.03 0.06 -0.09 0.12 -0.12 0.15c -0.06 0.03 -0.42 0.21 -0.84 0.36l -0.75 0.33l -0.03 2.43c 0 1.32 0 2.43 0.03 2.43c 0 0 0.27 -0.12 0.6 -0.24c 0.69 -0.27 0.78 -0.3 0.96 -0.15c 0.21 0.15 0.21 0.18 0.21 1.38c 0 1.02 0 1.11 -0.06 1.2c -0.03 0.06 -0.09 0.12 -0.12 0.15c -0.06 0.03 -0.42 0.21 -0.84 0.36l -0.75 0.33l -0.03 2.52c 0 2.28 -0.03 2.55 -0.06 2.64c -0.21 0.36 -0.72 0.36 -0.93 0c -0.03 -0.09 -0.06 -0.33 -0.06 -2.43l 0 -2.31l -1.29 0.51l -1.26 0.51l 0 2.43c 0 2.58 0 2.52 -0.15 2.67c -0.06 0.09 -0.27 0.18 -0.36 0.18c -0.12 0 -0.33 -0.09 -0.39 -0.18c -0.15 -0.15 -0.15 -0.09 -0.15 -2.43c 0 -1.23 0 -2.22 -0.03 -2.22c 0 0 -0.27 0.12 -0.6 0.24c -0.69 0.27 -0.78 0.3 -0.96 0.15c -0.21 -0.15 -0.21 -0.18 -0.21 -1.38c 0 -1.02 0 -1.11 0.06 -1.2c 0.03 -0.06 0.09 -0.12 0.12 -0.15c 0.06 -0.03 0.42 -0.21 0.84 -0.36l 0.78 -0.33l 0 -2.43c 0 -1.32 0 -2.43 -0.03 -2.43c 0 0 -0.27 0.12 -0.6 0.24c -0.69 0.27 -0.78 0.3 -0.96 0.15c -0.21 -0.15 -0.21 -0.18 -0.21 -1.38c 0 -1.02 0 -1.11 0.06 -1.2c 0.03 -0.06 0.09 -0.12 0.12 -0.15c 0.06 -0.03 0.42 -0.21 0.84 -0.36l 0.78 -0.33l 0 -2.52c 0 -2.28 0.03 -2.55 0.06 -2.64c 0.21 -0.36 0.72 -0.36 0.93 0c 0.03 0.09 0.06 0.33 0.06 2.43l 0.03 2.31l 1.26 -0.51l 1.26 -0.51l 0 -2.43c 0 -2.28 0 -2.43 0.06 -2.55c 0.06 -0.12 0.12 -0.18 0.27 -0.24zm -0.33 10.65l 0 -2.43l -1.29 0.51l -1.26 0.51l 0 2.46l 0 2.43l 0.09 -0.03c 0.06 -0.03 0.63 -0.27 1.29 -0.51l 1.17 -0.48l 0 -2.46z&quot;&gt;&lt;/path&gt;&lt;path data-name=&quot;accidentals.sharp&quot; d=&quot;M 75.281 74.352c 0.21 -0.12 0.54 -0.03 0.66 0.24c 0.06 0.12 0.06 0.21 0.06 2.31c 0 1.23 0 2.22 0.03 2.22c 0 0 0.27 -0.12 0.6 -0.24c 0.69 -0.27 0.78 -0.3 0.96 -0.15c 0.21 0.15 0.21 0.18 0.21 1.38c 0 1.02 0 1.11 -0.06 1.2c -0.03 0.06 -0.09 0.12 -0.12 0.15c -0.06 0.03 -0.42 0.21 -0.84 0.36l -0.75 0.33l -0.03 2.43c 0 1.32 0 2.43 0.03 2.43c 0 0 0.27 -0.12 0.6 -0.24c 0.69 -0.27 0.78 -0.3 0.96 -0.15c 0.21 0.15 0.21 0.18 0.21 1.38c 0 1.02 0 1.11 -0.06 1.2c -0.03 0.06 -0.09 0.12 -0.12 0.15c -0.06 0.03 -0.42 0.21 -0.84 0.36l -0.75 0.33l -0.03 2.52c 0 2.28 -0.03 2.55 -0.06 2.64c -0.21 0.36 -0.72 0.36 -0.93 0c -0.03 -0.09 -0.06 -0.33 -0.06 -2.43l 0 -2.31l -1.29 0.51l -1.26 0.51l 0 2.43c 0 2.58 0 2.52 -0.15 2.67c -0.06 0.09 -0.27 0.18 -0.36 0.18c -0.12 0 -0.33 -0.09 -0.39 -0.18c -0.15 -0.15 -0.15 -0.09 -0.15 -2.43c 0 -1.23 0 -2.22 -0.03 -2.22c 0 0 -0.27 0.12 -0.6 0.24c -0.69 0.27 -0.78 0.3 -0.96 0.15c -0.21 -0.15 -0.21 -0.18 -0.21 -1.38c 0 -1.02 0 -1.11 0.06 -1.2c 0.03 -0.06 0.09 -0.12 0.12 -0.15c 0.06 -0.03 0.42 -0.21 0.84 -0.36l 0.78 -0.33l 0 -2.43c 0 -1.32 0 -2.43 -0.03 -2.43c 0 0 -0.27 0.12 -0.6 0.24c -0.69 0.27 -0.78 0.3 -0.96 0.15c -0.21 -0.15 -0.21 -0.18 -0.21 -1.38c 0 -1.02 0 -1.11 0.06 -1.2c 0.03 -0.06 0.09 -0.12 0.12 -0.15c 0.06 -0.03 0.42 -0.21 0.84 -0.36l 0.78 -0.33l 0 -2.52c 0 -2.28 0.03 -2.55 0.06 -2.64c 0.21 -0.36 0.72 -0.36 0.93 0c 0.03 0.09 0.06 0.33 0.06 2.43l 0.03 2.31l 1.26 -0.51l 1.26 -0.51l 0 -2.43c 0 -2.28 0 -2.43 0.06 -2.55c 0.06 -0.12 0.12 -0.18 0.27 -0.24zm -0.33 10.65l 0 -2.43l -1.29 0.51l -1.26 0.51l 0 2.46l 0 2.43l 0.09 -0.03c 0.06 -0.03 0.63 -0.27 1.29 -0.51l 1.17 -0.48l 0 -2.46z&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;staff-extra time-signature&quot;&gt;&lt;path data-name=&quot;3&quot; d=&quot;M 92.611 89.947c 0.3 -0.03 1.41 0 1.83 0.06c 2.22 0.3 3.51 1.32 3.72 2.91c 0.03 0.33 0.03 1.26 -0.03 1.65c -0.12 0.84 -0.48 1.47 -1.05 1.77c -0.27 0.15 -0.36 0.24 -0.45 0.39c -0.09 0.21 -0.09 0.36 0 0.57c 0.09 0.15 0.18 0.24 0.51 0.39c 0.75 0.42 1.23 1.14 1.41 2.13c 0.06 0.42 0.06 1.35 0 1.71c -0.18 0.81 -0.48 1.38 -1.02 1.95c -0.75 0.72 -1.8 1.2 -3.18 1.38c -0.42 0.06 -1.56 0.06 -1.95 0c -1.89 -0.33 -3.18 -1.29 -3.51 -2.64c -0.03 -0.12 -0.03 -0.33 -0.03 -0.6c 0 -0.36 0 -0.42 0.06 -0.63c 0.12 -0.3 0.27 -0.51 0.51 -0.75c 0.24 -0.24 0.45 -0.39 0.75 -0.51c 0.21 -0.06 0.27 -0.06 0.6 -0.06c 0.33 0 0.39 0 0.6 0.06c 0.3 0.12 0.51 0.27 0.75 0.51c 0.36 0.33 0.57 0.75 0.6 1.2c 0 0.21 0 0.27 -0.06 0.42c -0.09 0.18 -0.12 0.24 -0.54 0.54c -0.51 0.36 -0.63 0.54 -0.6 0.87c 0.06 0.54 0.54 0.9 1.38 0.99c 0.36 0.06 0.72 0.03 0.96 -0.06c 0.81 -0.27 1.29 -1.23 1.44 -2.79c 0.03 -0.45 0.03 -1.95 -0.03 -2.37c -0.09 -0.75 -0.33 -1.23 -0.75 -1.44c -0.33 -0.18 -0.45 -0.18 -1.98 -0.18c -1.35 0 -1.41 0 -1.5 -0.06c -0.18 -0.12 -0.24 -0.39 -0.12 -0.6c 0.12 -0.15 0.15 -0.15 1.68 -0.15c 1.5 0 1.62 0 1.89 -0.15c 0.18 -0.09 0.42 -0.36 0.54 -0.57c 0.18 -0.42 0.27 -0.9 0.3 -1.95c 0.03 -1.2 -0.06 -1.8 -0.36 -2.37c -0.24 -0.48 -0.63 -0.81 -1.14 -0.96c -0.3 -0.06 -1.08 -0.06 -1.38 0.03c -0.6 0.15 -0.9 0.42 -0.96 0.84c -0.03 0.3 0.06 0.45 0.63 0.84c 0.33 0.24 0.42 0.39 0.45 0.63c 0.03 0.72 -0.57 1.5 -1.32 1.65c -1.05 0.27 -2.1 -0.57 -2.1 -1.65c 0 -0.45 0.15 -0.96 0.39 -1.38c 0.12 -0.21 0.54 -0.63 0.81 -0.81c 0.57 -0.42 1.38 -0.69 2.25 -0.81z&quot;&gt;&lt;/path&gt;&lt;path data-name=&quot;4&quot; d=&quot;M 96.441 105.477c 0.27 -0.09 0.42 -0.12 0.54 -0.03c 0.09 0.06 0.15 0.21 0.15 0.3c -0.03 0.06 -1.92 2.31 -4.23 5.04c -2.31 2.73 -4.23 4.98 -4.26 5.01c -0.03 0.06 0.12 0.06 2.55 0.06l 2.61 0l 0 -2.37c 0 -2.19 0.03 -2.37 0.06 -2.46c 0.03 -0.06 0.21 -0.18 0.57 -0.42c 1.08 -0.72 1.38 -1.08 1.86 -2.16c 0.12 -0.3 0.24 -0.54 0.27 -0.57c 0.12 -0.12 0.39 -0.06 0.45 0.12c 0.06 0.09 0.06 0.57 0.06 3.96l 0 3.9l 1.08 0c 1.05 0 1.11 0 1.2 0.06c 0.24 0.15 0.24 0.54 0 0.69c -0.09 0.06 -0.15 0.06 -1.2 0.06l -1.08 0l 0 0.33c 0 0.57 0.09 1.11 0.3 1.53c 0.36 0.75 0.93 1.17 1.68 1.26c 0.3 0.03 0.39 0.09 0.39 0.3c 0 0.15 -0.03 0.18 -0.09 0.24c -0.06 0.06 -0.09 0.06 -0.48 0.06c -0.42 0 -0.69 -0.03 -2.1 -0.24c -0.9 -0.15 -1.77 -0.15 -2.67 0c -1.41 0.21 -1.68 0.24 -2.1 0.24c -0.39 0 -0.42 0 -0.48 -0.06c -0.06 -0.06 -0.06 -0.09 -0.06 -0.24c 0 -0.21 0.06 -0.27 0.36 -0.3c 0.75 -0.09 1.32 -0.51 1.68 -1.26c 0.21 -0.42 0.3 -0.96 0.3 -1.53l 0 -0.33l -2.7 0c -2.91 0 -2.85 0 -3.09 -0.15c -0.18 -0.12 -0.3 -0.39 -0.27 -0.54c 0.03 -0.06 0.18 -0.24 0.33 -0.45c 0.75 -0.9 1.59 -2.07 2.13 -3.03c 0.33 -0.54 0.84 -1.62 1.05 -2.16c 0.57 -1.41 0.84 -2.64 0.9 -4.05c 0.03 -0.63 0.06 -0.72 0.24 -0.81l 0.12 -0.06l 0.45 0.12c 0.66 0.18 1.02 0.24 1.47 0.27c 0.6 0.03 1.23 -0.09 2.01 -0.33z&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;bar&quot;&gt;&lt;path d=&quot;M 114.6 120.42L 114.6 89.42L 115.2 89.42L 115.2 120.42z&quot; data-name=&quot;bar&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;note&quot; selectable=&quot;false&quot; data-index=&quot;0&quot;&gt;&lt;path data-name=&quot;e&amp;#x27;&quot; d=&quot;M 131.686 62.117000000000004c 0.36 -0.03 1.2 0 1.53 0.06c 1.17 0.24 1.89 0.84 2.16 1.83c 0.06 0.18 0.06 0.3 0.06 0.66c 0 0.45 0 0.63 -0.15 1.08c -0.66 2.04 -3.06 3.93 -5.52 4.38c -0.54 0.09 -1.44 0.09 -1.83 0.03c -1.23 -0.27 -1.98 -0.87 -2.25 -1.86c -0.06 -0.18 -0.06 -0.3 -0.06 -0.66c 0 -0.45 0 -0.63 0.15 -1.08c 0.24 -0.78 0.75 -1.53 1.44 -2.22c 1.2 -1.2 2.85 -2.01 4.47 -2.22z&quot; class=&quot;abcjs-notehead&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 123.6 65.82 L 137.41 65.82 L 137.41 66.52 L 123.6 66.52 z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot; data-name=&quot;ledger&quot; class=&quot;abcjs-ledger&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 123.6 73.57 L 137.41 73.57 L 137.41 74.27 L 123.6 74.27 z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot; data-name=&quot;ledger&quot; class=&quot;abcjs-ledger&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 123.6 81.32 L 137.41 81.32 L 137.41 82.02 L 123.6 82.02 z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot; data-name=&quot;ledger&quot; class=&quot;abcjs-ledger&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 125.6 102.98L 125.6 66.94L 126.2 66.94L 126.2 102.98z&quot; class=&quot;abcjs-stem&quot; data-name=&quot;stem&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;note&quot; selectable=&quot;false&quot; data-index=&quot;1&quot;&gt;&lt;path data-name=&quot;d&amp;#x27;&quot; d=&quot;M 166.84466827403048 65.992c 0.36 -0.03 1.2 0 1.53 0.06c 1.17 0.24 1.89 0.84 2.16 1.83c 0.06 0.18 0.06 0.3 0.06 0.66c 0 0.45 0 0.63 -0.15 1.08c -0.66 2.04 -3.06 3.93 -5.52 4.38c -0.54 0.09 -1.44 0.09 -1.83 0.03c -1.23 -0.27 -1.98 -0.87 -2.25 -1.86c -0.06 -0.18 -0.06 -0.3 -0.06 -0.66c 0 -0.45 0 -0.63 0.15 -1.08c 0.24 -0.78 0.75 -1.53 1.44 -2.22c 1.2 -1.2 2.85 -2.01 4.47 -2.22z&quot; class=&quot;abcjs-notehead&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 158.75 73.57 L 172.56 73.57 L 172.56 74.27 L 158.75 74.27 z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot; data-name=&quot;ledger&quot; class=&quot;abcjs-ledger&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 158.75 81.32 L 172.56 81.32 L 172.56 82.02 L 158.75 82.02 z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot; data-name=&quot;ledger&quot; class=&quot;abcjs-ledger&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 160.75 102.98L 160.75 70.82L 161.35 70.82L 161.35 102.98z&quot; class=&quot;abcjs-stem&quot; data-name=&quot;stem&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;note&quot; selectable=&quot;false&quot; data-index=&quot;2&quot;&gt;&lt;path data-name=&quot;f&quot; d=&quot;M 202.00333654806096 85.367c 0.36 -0.03 1.2 0 1.53 0.06c 1.17 0.24 1.89 0.84 2.16 1.83c 0.06 0.18 0.06 0.3 0.06 0.66c 0 0.45 0 0.63 -0.15 1.08c -0.66 2.04 -3.06 3.93 -5.52 4.38c -0.54 0.09 -1.44 0.09 -1.83 0.03c -1.23 -0.27 -1.98 -0.87 -2.25 -1.86c -0.06 -0.18 -0.06 -0.3 -0.06 -0.66c 0 -0.45 0 -0.63 0.15 -1.08c 0.24 -0.78 0.75 -1.53 1.44 -2.22c 1.2 -1.2 2.85 -2.01 4.47 -2.22z&quot; class=&quot;abcjs-notehead&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 195.91 116.54L 195.91 90.71L 196.91 90.71L 196.91 116.54z&quot; class=&quot;abcjs-stem&quot; data-name=&quot;stem&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;note&quot; selectable=&quot;false&quot; data-index=&quot;3&quot;&gt;&lt;path data-name=&quot;g&quot; d=&quot;M 251.7252020561715 81.492c 0.36 -0.03 1.2 0 1.53 0.06c 1.17 0.24 1.89 0.84 2.16 1.83c 0.06 0.18 0.06 0.3 0.06 0.66c 0 0.45 0 0.63 -0.15 1.08c -0.66 2.04 -3.06 3.93 -5.52 4.38c -0.54 0.09 -1.44 0.09 -1.83 0.03c -1.23 -0.27 -1.98 -0.87 -2.25 -1.86c -0.06 -0.18 -0.06 -0.3 -0.06 -0.66c 0 -0.45 0 -0.63 0.15 -1.08c 0.24 -0.78 0.75 -1.53 1.44 -2.22c 1.2 -1.2 2.85 -2.01 4.47 -2.22z&quot; class=&quot;abcjs-notehead&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 245.64 112.67L 245.64 86.83L 246.64 86.83L 246.64 112.67z&quot; class=&quot;abcjs-stem&quot; data-name=&quot;stem&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;bar&quot;&gt;&lt;path d=&quot;M 295.36 120.42L 295.36 89.42L 295.96 89.42L 295.96 120.42z&quot; data-name=&quot;bar&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;note&quot; selectable=&quot;false&quot; data-index=&quot;4&quot;&gt;&lt;path data-name=&quot;c&amp;#x27;&quot; d=&quot;M 312.447067564282 69.867c 0.36 -0.03 1.2 0 1.53 0.06c 1.17 0.24 1.89 0.84 2.16 1.83c 0.06 0.18 0.06 0.3 0.06 0.66c 0 0.45 0 0.63 -0.15 1.08c -0.66 2.04 -3.06 3.93 -5.52 4.38c -0.54 0.09 -1.44 0.09 -1.83 0.03c -1.23 -0.27 -1.98 -0.87 -2.25 -1.86c -0.06 -0.18 -0.06 -0.3 -0.06 -0.66c 0 -0.45 0 -0.63 0.15 -1.08c 0.24 -0.78 0.75 -1.53 1.44 -2.22c 1.2 -1.2 2.85 -2.01 4.47 -2.22z&quot; class=&quot;abcjs-notehead&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 304.36 73.57 L 318.17 73.57 L 318.17 74.27 L 304.36 74.27 z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot; data-name=&quot;ledger&quot; class=&quot;abcjs-ledger&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 304.36 81.32 L 318.17 81.32 L 318.17 82.02 L 304.36 82.02 z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot; data-name=&quot;ledger&quot; class=&quot;abcjs-ledger&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 306.36 102.98L 306.36 74.69L 306.96 74.69L 306.96 102.98z&quot; class=&quot;abcjs-stem&quot; data-name=&quot;stem&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;note&quot; selectable=&quot;false&quot; data-index=&quot;5&quot;&gt;&lt;path data-name=&quot;b&quot; d=&quot;M 347.60573583831246 73.742c 0.36 -0.03 1.2 0 1.53 0.06c 1.17 0.24 1.89 0.84 2.16 1.83c 0.06 0.18 0.06 0.3 0.06 0.66c 0 0.45 0 0.63 -0.15 1.08c -0.66 2.04 -3.06 3.93 -5.52 4.38c -0.54 0.09 -1.44 0.09 -1.83 0.03c -1.23 -0.27 -1.98 -0.87 -2.25 -1.86c -0.06 -0.18 -0.06 -0.3 -0.06 -0.66c 0 -0.45 0 -0.63 0.15 -1.08c 0.24 -0.78 0.75 -1.53 1.44 -2.22c 1.2 -1.2 2.85 -2.01 4.47 -2.22z&quot; class=&quot;abcjs-notehead&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 339.52 81.32 L 353.33 81.32 L 353.33 82.02 L 339.52 82.02 z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot; data-name=&quot;ledger&quot; class=&quot;abcjs-ledger&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 341.52 106.79L 341.52 78.57L 342.12 78.57L 342.12 106.79z&quot; class=&quot;abcjs-stem&quot; data-name=&quot;stem&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;note&quot; selectable=&quot;false&quot; data-index=&quot;6&quot;&gt;&lt;path data-name=&quot;d&quot; d=&quot;M 382.7644041123429 93.117c 0.36 -0.03 1.2 0 1.53 0.06c 1.17 0.24 1.89 0.84 2.16 1.83c 0.06 0.18 0.06 0.3 0.06 0.66c 0 0.45 0 0.63 -0.15 1.08c -0.66 2.04 -3.06 3.93 -5.52 4.38c -0.54 0.09 -1.44 0.09 -1.83 0.03c -1.23 -0.27 -1.98 -0.87 -2.25 -1.86c -0.06 -0.18 -0.06 -0.3 -0.06 -0.66c 0 -0.45 0 -0.63 0.15 -1.08c 0.24 -0.78 0.75 -1.53 1.44 -2.22c 1.2 -1.2 2.85 -2.01 4.47 -2.22z&quot; class=&quot;abcjs-notehead&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 376.67 124.29L 376.67 98.46L 377.67 98.46L 377.67 124.29z&quot; class=&quot;abcjs-stem&quot; data-name=&quot;stem&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;note&quot; selectable=&quot;false&quot; data-index=&quot;7&quot;&gt;&lt;path data-name=&quot;e&quot; d=&quot;M 432.48626962045347 89.242c 0.36 -0.03 1.2 0 1.53 0.06c 1.17 0.24 1.89 0.84 2.16 1.83c 0.06 0.18 0.06 0.3 0.06 0.66c 0 0.45 0 0.63 -0.15 1.08c -0.66 2.04 -3.06 3.93 -5.52 4.38c -0.54 0.09 -1.44 0.09 -1.83 0.03c -1.23 -0.27 -1.98 -0.87 -2.25 -1.86c -0.06 -0.18 -0.06 -0.3 -0.06 -0.66c 0 -0.45 0 -0.63 0.15 -1.08c 0.24 -0.78 0.75 -1.53 1.44 -2.22c 1.2 -1.2 2.85 -2.01 4.47 -2.22z&quot; class=&quot;abcjs-notehead&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 426.4 120.42L 426.4 94.58L 427.4 94.58L 427.4 120.42z&quot; class=&quot;abcjs-stem&quot; data-name=&quot;stem&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;bar&quot;&gt;&lt;path d=&quot;M 476.12 120.42L 476.12 89.42L 476.72 89.42L 476.72 120.42z&quot; data-name=&quot;bar&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;note&quot; selectable=&quot;false&quot; data-index=&quot;8&quot;&gt;&lt;path data-name=&quot;b&quot; d=&quot;M 493.20813512856404 73.742c 0.36 -0.03 1.2 0 1.53 0.06c 1.17 0.24 1.89 0.84 2.16 1.83c 0.06 0.18 0.06 0.3 0.06 0.66c 0 0.45 0 0.63 -0.15 1.08c -0.66 2.04 -3.06 3.93 -5.52 4.38c -0.54 0.09 -1.44 0.09 -1.83 0.03c -1.23 -0.27 -1.98 -0.87 -2.25 -1.86c -0.06 -0.18 -0.06 -0.3 -0.06 -0.66c 0 -0.45 0 -0.63 0.15 -1.08c 0.24 -0.78 0.75 -1.53 1.44 -2.22c 1.2 -1.2 2.85 -2.01 4.47 -2.22z&quot; class=&quot;abcjs-notehead&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 485.12 81.32 L 498.93 81.32 L 498.93 82.02 L 485.12 82.02 z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot; data-name=&quot;ledger&quot; class=&quot;abcjs-ledger&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 487.12 106.85L 487.12 78.57L 487.72 78.57L 487.72 106.85z&quot; class=&quot;abcjs-stem&quot; data-name=&quot;stem&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;note&quot; selectable=&quot;false&quot; data-index=&quot;9&quot;&gt;&lt;path data-name=&quot;a&quot; d=&quot;M 528.3668034025945 77.617c 0.36 -0.03 1.2 0 1.53 0.06c 1.17 0.24 1.89 0.84 2.16 1.83c 0.06 0.18 0.06 0.3 0.06 0.66c 0 0.45 0 0.63 -0.15 1.08c -0.66 2.04 -3.06 3.93 -5.52 4.38c -0.54 0.09 -1.44 0.09 -1.83 0.03c -1.23 -0.27 -1.98 -0.87 -2.25 -1.86c -0.06 -0.18 -0.06 -0.3 -0.06 -0.66c 0 -0.45 0 -0.63 0.15 -1.08c 0.24 -0.78 0.75 -1.53 1.44 -2.22c 1.2 -1.2 2.85 -2.01 4.47 -2.22z&quot; class=&quot;abcjs-notehead&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 520.28 81.32 L 534.09 81.32 L 534.09 82.02 L 520.28 82.02 z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot; data-name=&quot;ledger&quot; class=&quot;abcjs-ledger&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 522.28 110.66L 522.28 82.44L 522.88 82.44L 522.88 110.66z&quot; class=&quot;abcjs-stem&quot; data-name=&quot;stem&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;note&quot; selectable=&quot;false&quot; data-index=&quot;10&quot;&gt;&lt;path data-name=&quot;c&quot; d=&quot;M 563.525471676625 96.992c 0.36 -0.03 1.2 0 1.53 0.06c 1.17 0.24 1.89 0.84 2.16 1.83c 0.06 0.18 0.06 0.3 0.06 0.66c 0 0.45 0 0.63 -0.15 1.08c -0.66 2.04 -3.06 3.93 -5.52 4.38c -0.54 0.09 -1.44 0.09 -1.83 0.03c -1.23 -0.27 -1.98 -0.87 -2.25 -1.86c -0.06 -0.18 -0.06 -0.3 -0.06 -0.66c 0 -0.45 0 -0.63 0.15 -1.08c 0.24 -0.78 0.75 -1.53 1.44 -2.22c 1.2 -1.2 2.85 -2.01 4.47 -2.22z&quot; class=&quot;abcjs-notehead&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 557.44 128.17L 557.44 102.33L 558.44 102.33L 558.44 128.17z&quot; class=&quot;abcjs-stem&quot; data-name=&quot;stem&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;note&quot; selectable=&quot;false&quot; data-index=&quot;11&quot;&gt;&lt;path data-name=&quot;e&quot; d=&quot;M 613.2473371847356 89.242c 0.36 -0.03 1.2 0 1.53 0.06c 1.17 0.24 1.89 0.84 2.16 1.83c 0.06 0.18 0.06 0.3 0.06 0.66c 0 0.45 0 0.63 -0.15 1.08c -0.66 2.04 -3.06 3.93 -5.52 4.38c -0.54 0.09 -1.44 0.09 -1.83 0.03c -1.23 -0.27 -1.98 -0.87 -2.25 -1.86c -0.06 -0.18 -0.06 -0.3 -0.06 -0.66c 0 -0.45 0 -0.63 0.15 -1.08c 0.24 -0.78 0.75 -1.53 1.44 -2.22c 1.2 -1.2 2.85 -2.01 4.47 -2.22z&quot; class=&quot;abcjs-notehead&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 607.16 120.42L 607.16 94.58L 608.16 94.58L 608.16 120.42z&quot; class=&quot;abcjs-stem&quot; data-name=&quot;stem&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;bar&quot;&gt;&lt;path d=&quot;M 656.88 120.42L 656.88 89.42L 657.48 89.42L 657.48 120.42z&quot; data-name=&quot;bar&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;note&quot; selectable=&quot;false&quot; data-index=&quot;12&quot;&gt;&lt;path data-name=&quot;dots.dot&quot; d=&quot;M 682.5692026928461 76.112c 0.09 -0.03 0.27 -0.06 0.39 -0.06c 0.96 0 1.74 0.78 1.74 1.71c 0 0.96 -0.78 1.74 -1.71 1.74c -0.96 0 -1.74 -0.78 -1.74 -1.71c 0 -0.78 0.54 -1.5 1.32 -1.68z&quot;&gt;&lt;/path&gt;&lt;path data-name=&quot;a&quot; d=&quot;M 675.3192026928461 77.617c 0.06 -0.03 0.27 -0.03 0.48 -0.03c 1.05 0 1.71 0.24 2.1 0.81c 0.42 0.6 0.45 1.35 0.18 2.4c -0.42 1.59 -1.14 2.73 -2.16 3.39c -1.41 0.93 -3.18 1.44 -5.4 1.53c -1.17 0.03 -1.89 -0.21 -2.28 -0.81c -0.42 -0.6 -0.45 -1.35 -0.18 -2.4c 0.42 -1.59 1.14 -2.73 2.16 -3.39c 0.63 -0.42 1.23 -0.72 1.98 -0.96c 0.9 -0.3 1.65 -0.42 3.12 -0.54zm 1.29 0.87c -0.27 -0.09 -0.63 -0.12 -0.9 -0.03c -0.72 0.24 -1.53 0.69 -3.27 1.8c -2.34 1.5 -3.3 2.25 -3.57 2.79c -0.36 0.72 -0.06 1.5 0.66 1.77c 0.24 0.12 0.69 0.09 0.99 0c 0.84 -0.3 1.92 -0.93 4.14 -2.37c 1.62 -1.08 2.37 -1.71 2.61 -2.19c 0.36 -0.72 0.06 -1.5 -0.66 -1.77z&quot; class=&quot;abcjs-notehead&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 667.88 108.79L 667.88 82.96L 668.88 82.96L 668.88 108.79z&quot; class=&quot;abcjs-stem&quot; data-name=&quot;stem&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M 665.88 81.32 L 680.25 81.32 L 680.25 82.02 L 665.88 82.02 z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot; data-name=&quot;ledger&quot; class=&quot;abcjs-ledger&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;g fill=&quot;currentColor&quot; stroke=&quot;none&quot; data-name=&quot;bar&quot;&gt;&lt;path d=&quot;M 754 120.42L 754 89.42L 754.6 89.42L 754.6 120.42z&quot; data-name=&quot;bar&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;path d=&quot;M125.6 104.92 L161.35 104.92L161.35 101.05 L125.6 101.05z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot; class=&quot;&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M306.36 104.92 L342.12 108.79L342.12 104.92 L306.36 101.05z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot; class=&quot;&quot;&gt;&lt;/path&gt;&lt;path d=&quot;M487.12 108.79 L522.88 112.67L522.88 108.8 L487.12 104.92z&quot; stroke=&quot;none&quot; fill=&quot;currentColor&quot; class=&quot;&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/div&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;remark-abcjs&lt;/code&gt; is a &lt;a href=&quot;https://github.com/remarkjs&quot;&gt;Remark&lt;/a&gt; plugin to render sheet music written in &lt;a href=&quot;https://abcnotation.com/&quot;&gt;ABC notation&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I wanted to learn more about the &lt;a href=&quot;https://unifiedjs.com/&quot;&gt;Unified&lt;/a&gt; ecosystem by writing a plugin for it, and this seemed like an interesting challenge. I also figured I might end up using it on my site if I ever get around to posting music-related content.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The plugin looks for nodes in the syntax tree with type &lt;code&gt;code&lt;/code&gt; and language &lt;code&gt;abc&lt;/code&gt;. This means you can write ABC notation as:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot; data-language=&quot;md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token code&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;```&lt;/span&gt;&lt;span class=&quot;token code-language&quot;&gt;abc&lt;/span&gt;
&lt;span class=&quot;token code-block language-abc&quot;&gt;X: 1
T: Nokia Tune
M: 3/4
L: 1/8
K: Amaj
| e&apos;d&apos; f2 g2 | c&apos;b d2 e2 | ba c2 e2 | a6 |&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It then uses &lt;a href=&quot;https://paulrosen.github.io/abcjs/&quot;&gt;ABCJS&lt;/a&gt; to render the music to an SVG, storing the result in the &lt;code&gt;data&lt;/code&gt; property of the node so that &lt;code&gt;remark-rehype&lt;/code&gt; can render it as HTML.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;Works well enough that I’m using it successfully on my site. That said, compromises had to be made. ABCJS doesn’t support Node.js environments out of the box, so I had to use &lt;a href=&quot;https://github.com/ds300/patch-package&quot;&gt;patch-package&lt;/a&gt; to manually patch it, and then use a build script to include the patched version. My patch uses &lt;a href=&quot;https://github.com/jsdom/jsdom&quot;&gt;JSDOM&lt;/a&gt; to create a &lt;code&gt;document&lt;/code&gt; object for DOM manipulation.&lt;/p&gt;
&lt;p&gt;Also, this project showed me, to put it frankly, how broken the ES module rollout has become. The UnifiedJS collective has more or less entirely switched to pure ESM packages, which can’t be &lt;code&gt;require()&lt;/code&gt;’d. On the other hand, Gatsby is still purely CommonJS. As a result, any Gatsby site has to pin an old version of &lt;code&gt;remark&lt;/code&gt;/&lt;code&gt;rehype&lt;/code&gt; and friends. I was primarily developing this plugin for my own site, but I wanted to support the latest standards, so I used &lt;a href=&quot;https://babeljs.io/&quot;&gt;Babel&lt;/a&gt; to transpile the ES module source to CommonJS. This added complexity to the build process, and I had to pin the CommonJS dual mode versions of all the UnifiedJS packages I depended on. This ended up being kind of the worst of both worlds.&lt;/p&gt;
&lt;p&gt;Overall, though, I’m happy I took on this project. I ended up with something useful and I learned a lot about the inner workings of &lt;code&gt;remark&lt;/code&gt; and the rest of the Unified ecosystem.&lt;/p&gt;</content:encoded></item><item><title>Building a text processing pipeline with Unified</title><link>https://breq.dev/2022/01/03/unified/</link><guid isPermaLink="true">https://breq.dev/2022/01/03/unified/</guid><description>Spellcheck, grammar check, Markdown, HTML, and more!</description><pubDate>Mon, 03 Jan 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://unifiedjs.com/&quot;&gt;Unified&lt;/a&gt; is a set of software packages designed to work with text data. Many projects including &lt;a href=&quot;https://gatsbyjs.com/&quot;&gt;Gatsby&lt;/a&gt; uses it to render markdown. In this post, I’ll walk through setting up a processor using Unified. We’ll start by just processing &lt;code&gt;.txt&lt;/code&gt; files, but by the end, we’ll have a working compiler from Markdown to HTML. We’ll also write several of our own plugins for Unified.&lt;/p&gt;
&lt;p&gt;I’m going to assume some basic familiarity with JavaScript and NPM, but my hope is that those new to Node or modern JavaScript will still be able to follow along. That said, the topics will gradually get more difficult as the post continues.&lt;/p&gt;
&lt;h1 id=&quot;setup&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#setup&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Setup&lt;/h1&gt;
&lt;p&gt;Unified is written as JavaScript modules intended to be run with NodeJS. Start by setting up a new &lt;code&gt;npm&lt;/code&gt; package.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; unified-example
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; unified-example
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; init &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will generate a &lt;code&gt;package.json&lt;/code&gt; file for you.&lt;/p&gt;
&lt;p&gt;The Unified ecosystem uses ECMAScript modules exclusively. However, Node defaults to using CommonJS modules. We will need to modify the &lt;code&gt;package.json&lt;/code&gt; file to enable modules. Open up this folder in your text editor of choice, and add a &lt;code&gt;&quot;type&quot;: &quot;module&quot;&lt;/code&gt; declaration at the end:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot; data-language=&quot;json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;...&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;module&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we can install Unified itself.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; unified
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s create an &lt;code&gt;index.js&lt;/code&gt; file for our code.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; unified &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unified&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, add an &lt;code&gt;npm run build&lt;/code&gt; command to our &lt;code&gt;package.json&lt;/code&gt;. Modify the &lt;code&gt;&quot;scripts&quot;&lt;/code&gt; section to add the following.&lt;/p&gt;
&lt;pre class=&quot;language-json&quot; data-language=&quot;json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;node ./index.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    ...
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now run our pipeline with &lt;code&gt;npm run build&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And… nothing happened! That’s okay: We aren’t feeding anything to our processor yet.&lt;/p&gt;
&lt;h1 id=&quot;input-and-output&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#input-and-output&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Input and Output&lt;/h1&gt;
&lt;p&gt;Let’s configure our processor to read from a &lt;code&gt;src&lt;/code&gt; directory, and write to a &lt;code&gt;dist&lt;/code&gt; directory.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; src
&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; dist
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we need some way to run multiple files through our pipeline. We can use &lt;code&gt;unified-engine&lt;/code&gt; for this. The engine will select all files from our source paths, run them through the processor, and output them to the destination path.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; unified-engine
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, let’s use it in our code.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; unified &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unified&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; engine &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unified-engine&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;engine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        processor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./src/**/*.txt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./dist&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;unified-engine&lt;/code&gt; will call a callback function when it is finished processing all files. Unfortunately, it doesn’t support &lt;code&gt;await&lt;/code&gt;-ing the result directly. So, we use &lt;code&gt;await new Promise&lt;/code&gt; to wait for the callback. If you aren’t familiar with Promises, you can think of this as “waiting for the callback to be called” instead of writing a separate callback function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now, if we run this… still nothing. We don’t have any files to process. We can make an &lt;code&gt;index.txt&lt;/code&gt; file in the &lt;code&gt;src&lt;/code&gt; directory, and run it again:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello world&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; src/index.txt
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run build
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;./src/index.txt
  1:1  error  TypeError: Cannot `parse` without `Parser`
    at assertParser (file:///Users/breq/code/unified-example/node_modules/unified/lib/index.js:507:11)
    at Function.parse (file:///Users/breq/code/unified-example/node_modules/unified/lib/index.js:265:5)
    at parse (file:///Users/breq/code/unified-example/node_modules/unified-engine/lib/file-pipeline/parse.js:50:36)
    at wrapped (file:///Users/breq/code/unified-example/node_modules/trough/index.js:111:16)
    at next (file:///Users/breq/code/unified-example/node_modules/trough/index.js:62:23)
    at done (file:///Users/breq/code/unified-example/node_modules/trough/index.js:145:7)
    at file:///Users/breq/code/unified-example/node_modules/unified-engine/lib/file-pipeline/configure.js:76:5
    at file:///Users/breq/code/unified-example/node_modules/unified-engine/lib/configuration.js:138:11

✖ 1 error
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The pipeline is trying to process our input file, but it doesn’t have any parser configured.&lt;/p&gt;
&lt;h1 id=&quot;parsers&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#parsers&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Parsers&lt;/h1&gt;
&lt;p&gt;Parsers are what Unified uses to convert an input file into a syntax tree. They exist for plaintext (&lt;code&gt;.txt&lt;/code&gt; files), Markdown, and HTML.&lt;/p&gt;
&lt;p&gt;In general, packages to work with Unified are split into three groups: &lt;code&gt;remark&lt;/code&gt; for handling markdown, &lt;code&gt;rehype&lt;/code&gt; for handling HTML, and &lt;code&gt;retext&lt;/code&gt; for handling plain text.&lt;/p&gt;
&lt;p&gt;For our example, we’re reading in a &lt;code&gt;.txt&lt;/code&gt; file. We can use a &lt;code&gt;retext&lt;/code&gt; plugin to convert it to a syntax tree. Currently, &lt;code&gt;retext&lt;/code&gt; plugins are available for English and Dutch, plus a catchall &lt;code&gt;retext-latin&lt;/code&gt; plugin for languages that use Latin-based scripts.&lt;/p&gt;
&lt;p&gt;Let’s assume we’re going to work exclusively with English.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; retext-english
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now, we can add our parser to our pipeline.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; unified &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unified&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; engine &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unified-engine&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextEnglish &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-english&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextEnglish&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Give it another &lt;code&gt;npm run build&lt;/code&gt; and…&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;./src/index.txt
  1:1  error  TypeError: Cannot `stringify` without `Compiler`
    at assertCompiler (file:///Users/breq/code/unified-example/node_modules/unified/lib/index.js:520:11)
    at Function.stringify (file:///Users/breq/code/unified-example/node_modules/unified/lib/index.js:281:5)
    at stringify (file:///Users/breq/code/unified-example/node_modules/unified-engine/lib/file-pipeline/stringify.js:59:31)
    at wrapped (file:///Users/breq/code/unified-example/node_modules/trough/index.js:111:16)
    at next (file:///Users/breq/code/unified-example/node_modules/trough/index.js:62:23)
    at Object.run (file:///Users/breq/code/unified-example/node_modules/trough/index.js:33:5)
    at run (file:///Users/breq/code/unified-example/node_modules/unified-engine/lib/file-pipeline/index.js:57:10)
    at wrapped (file:///Users/breq/code/unified-example/node_modules/trough/index.js:111:16)
    at next (file:///Users/breq/code/unified-example/node_modules/trough/index.js:62:23)
    at done (file:///Users/breq/code/unified-example/node_modules/trough/index.js:145:7)

✖ 1 error
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our pipeline is processing our file, but it can’t stringify and save the result. This is where we need a compiler.&lt;/p&gt;
&lt;h1 id=&quot;compilers&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#compilers&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Compilers&lt;/h1&gt;
&lt;p&gt;Compilers are what Unified uses to convert a syntax tree back into a file. Just like with parsers, they exist for all sorts of markup languages. For now, let’s keep things simple and output the result as a &lt;code&gt;.txt&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Again, the &lt;code&gt;retext&lt;/code&gt; ecosystem will help us. We can use &lt;code&gt;retext-stringify&lt;/code&gt; as our compiler to output another &lt;code&gt;.txt&lt;/code&gt; file.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; retext-stringify
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; unified &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unified&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; engine &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unified-engine&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextEnglish &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-english&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextStringify &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-stringify&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextEnglish&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextStringify&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, our pipeline runs! We now have a &lt;code&gt;dist/index.txt&lt;/code&gt; file containing our “Hello world” text.&lt;/p&gt;
&lt;p&gt;…so what was the point of this? Right now, it seems like all we have is a complicated way to copy files between directories. But the intermediate syntax tree is where the magic happens—we can perform all sorts of processing steps on our text.&lt;/p&gt;
&lt;h1 id=&quot;syntax-trees&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#syntax-trees&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Syntax Trees&lt;/h1&gt;
&lt;p&gt;Before diving into what syntax trees let us do, let’s take a look at what one looks like.&lt;/p&gt;
&lt;p&gt;Syntax trees in Unified follow the &lt;a href=&quot;https://github.com/syntax-tree/unist&quot;&gt;unist&lt;/a&gt; specification. This spec defines &lt;em&gt;nodes&lt;/em&gt;, which can be either &lt;em&gt;parent&lt;/em&gt; nodes (which contain other nodes) or &lt;em&gt;literal&lt;/em&gt; nodes (which contain some specific value).&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;unist-util-inspect&lt;/code&gt; package is a useful tool for inspecting &lt;code&gt;unist&lt;/code&gt; syntax trees. Let’s add it to our pipeline.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; unist-util-inspect
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using this library is a bit tricky right now. &lt;code&gt;unist-util-inspect&lt;/code&gt; isn’t aware of any of the Unified tooling we have—it’s just a function that takes in a syntax tree. We need to hook into the pipeline somehow.&lt;/p&gt;
&lt;p&gt;To do this, we need to write our own plugin.&lt;/p&gt;
&lt;h2 id=&quot;making-a-plugin&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#making-a-plugin&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Making a Plugin&lt;/h2&gt;
&lt;p&gt;In the Unified ecosystem, a plugin is a function that takes in some options and returns another function. The returned function is then called on the syntax tree.&lt;/p&gt;
&lt;p&gt;Let’s write a plugin called &lt;code&gt;inspectPlugin&lt;/code&gt; that logs the syntax tree to the console.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inspect &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unist-util-inspect&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;inspectPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextEnglish&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextStringify&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inspectPlugin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not too bad, right? Writing our own plugin only took 5 lines of code. Now, if we run our pipeline again, we should see:&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;RootNode[2] (1:1-2:1, 0-12)
├─0 ParagraphNode[1] (1:1-1:12, 0-11)
│   └─0 SentenceNode[3] (1:1-1:12, 0-11)
│       ├─0 WordNode[1] (1:1-1:6, 0-5)
│       │   └─0 TextNode &quot;Hello&quot; (1:1-1:6, 0-5)
│       ├─1 WhiteSpaceNode &quot; &quot; (1:6-1:7, 5-6)
│       └─2 WordNode[1] (1:7-1:12, 6-11)
│           └─0 TextNode &quot;world&quot; (1:7-1:12, 6-11)
└─1 WhiteSpaceNode &quot;\n&quot; (1:12-2:1, 11-12)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the syntax tree that our pipeline built. Specifically, this is the tree that &lt;code&gt;retext-english&lt;/code&gt; created, and it’s what &lt;code&gt;retext-stringify&lt;/code&gt; used to compile our output file.&lt;/p&gt;
&lt;h1 id=&quot;text-processing&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#text-processing&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Text Processing&lt;/h1&gt;
&lt;h2 id=&quot;spell-checking&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#spell-checking&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Spell Checking&lt;/h2&gt;
&lt;p&gt;There are plenty of &lt;code&gt;retext&lt;/code&gt; plugins that can work with text. Let’s start by adding spell checking to our pipeline using &lt;code&gt;retext-spell&lt;/code&gt;. We also need to install a dictionary package: let’s use &lt;code&gt;dictionary-en&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; retext-spell dictionary-en
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now add&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextSpell &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-spell&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; dictionary &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dictionary-en&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to the imports, and add&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextSpell&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; dictionary &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to the processor. You might notice that we’re passing in an object to &lt;code&gt;.use&lt;/code&gt;. These are the configuration options for the plugin. Most plugins take in optional options of some sort, but in this case, &lt;code&gt;retext-spell&lt;/code&gt; requires the &lt;code&gt;dictionary&lt;/code&gt; option.&lt;/p&gt;
&lt;p&gt;Run our pipeline again, and nothing extra should happen. Let’s misspell some words and run it again!&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Ehllo world&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; src/index.txt
&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run build
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;  1:1-1:6  warning  `Ehllo` is misspelt; did you mean `Hello`?  ehllo  retext-spell

⚠ 1 warning
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And there you have it: spell checking!&lt;/p&gt;
&lt;h2 id=&quot;other-retext-plugins&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#other-retext-plugins&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Other ReText Plugins&lt;/h2&gt;
&lt;p&gt;We can pull in more plugins, too. But first, let’s get a bit more source text. I’m going to use a snippet of one of my project writeups:&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;The STM32 microcontroller this project used doesn&apos;t have any purpose-built
hardware for generating sounds (that I&apos;m aware of). So, the solution I
settled on was to manually generate a square wave by setting a GPIO pin
high, waiting for half the length of the waveform, setting it low, and
waiting for the rest of the waveform.

The biggest hurdle with this approach was accurate timing. The STM32 can
use interrupts to delay for a precise number of milliseconds, but
generating square waves at specific frequencies requires sub-millisecond
precision. The solution I came up with was to calibrate a busy-wait loop
when the code begins using the millisecond timer, then use that busy-wait
loop for sub-millisecond-precision delays. This yielded a decent-sounding
square wave, but the game audio still felt incomplete.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We should also probably stop logging the entire syntax tree to the console. Comment out the &lt;code&gt;console.log&lt;/code&gt; for now in our custom plugin.&lt;/p&gt;
&lt;p&gt;Let’s install some more prose plugins. I’m going to throw pretty much the entire suite of plugins into our pipeline.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; retext-contractions retext-diacritics retext-equality retext-indefinite-article retext-profanities retext-repeated-words retext-smartypants retext-quotes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our full code should now look like:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; unified &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unified&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; engine &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unified-engine&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextEnglish &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-english&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextStringify &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-stringify&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inspect &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unist-util-inspect&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextSpell &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-spell&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; dictionary &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dictionary-en&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextContractions &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-contractions&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextDiacritics &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-diacritics&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextEquality &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-equality&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextIndefiniteArticle &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-indefinite-article&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextProfanities &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-profanities&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextRepeatedWords &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-repeated-words&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextSmartypants &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-smartypants&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextQuotes &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-quotes&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;inspectPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// console.log(inspect(tree));&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextEnglish&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextStringify&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inspectPlugin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextSpell&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; dictionary &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextContractions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextDiacritics&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextEquality&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextIndefiniteArticle&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextProfanities&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextRepeatedWords&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextSmartypants&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextQuotes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;engine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        processor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./src/**/*.txt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./dist&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And run! The file was written to &lt;code&gt;./dist/index.txt&lt;/code&gt; successfully, but there were a few warnings:&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;./src/index.txt &gt; dist/index.txt
     1:5-1:10  warning  `STM32` is misspelt; did you mean `STM32nd`?                     stm32             retext-spell
    1:11-1:26  warning  `microcontroller` is misspelt                                    microcontroller   retext-spell
    1:45-1:52  warning  Expected the apostrophe in `doesn&apos;t` to be like this: `doesn’t`  smart-apostrophe  retext-contractions
  1:113-1:116  warning  Expected the apostrophe in `I&apos;m` to be like this: `I’m`          smart-apostrophe  retext-contractions
  1:210-1:214  warning  `GPIO` is misspelt; did you mean `GPO`?                          gpio              retext-spell
    3:64-3:69  warning  `STM32` is misspelt; did you mean `STM32nd`?                     stm32             retext-spell

⚠ 6 warnings
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few technical words (“STM32”, “microcontroller”, “GPIO”) are incorrectly detected as misspelled. We can add a personal dictionary to resolve this.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;STM32&lt;span class=&quot;token entity&quot; title=&quot;\n&quot;&gt;\n&lt;/span&gt;microcontroller&lt;span class=&quot;token entity&quot; title=&quot;\n&quot;&gt;\n&lt;/span&gt;GPIO&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; dictionary.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can configure &lt;code&gt;retext-spell&lt;/code&gt; to use our personal dictionary.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; fs &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fs/promises&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; personal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./dictionary.txt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextSpell&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    dictionary&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    personal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we only have quote errors remaining. &lt;code&gt;retext-contractions&lt;/code&gt; expects us to use smart apostrophes. &lt;code&gt;retext-smartypants&lt;/code&gt; adds those automatically. If you look at &lt;code&gt;dist/index.txt&lt;/code&gt;, you’ll see that &lt;code&gt;doesn&apos;t&lt;/code&gt; is now &lt;code&gt;doesn’t&lt;/code&gt;. So why is &lt;code&gt;retext-contractions&lt;/code&gt; complaining?&lt;/p&gt;
&lt;h2 id=&quot;plugin-types-and-plugin-order&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#plugin-types-and-plugin-order&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Plugin Types and Plugin Order&lt;/h2&gt;
&lt;p&gt;The issue is the order that our plugins are being used. Since &lt;code&gt;retext-contractions&lt;/code&gt; comes before &lt;code&gt;retext-smartypants&lt;/code&gt;, the smart quote insertion happens after the smart quotes are checked.&lt;/p&gt;
&lt;p&gt;However, you might notice that &lt;code&gt;retext-stringify&lt;/code&gt; is the second plugin we use, yet the other plugins modify the tree before it is stringified and written to disk. Why?&lt;/p&gt;
&lt;p&gt;The answer is that &lt;code&gt;retext-stringify&lt;/code&gt; works a bit differently than you’d expect. Instead of performing some operation on the tree directly, it configures the processor object, setting itself as the compiler. This means that even though the plugin is one of the first in the pipeline, nothing is executed until the pipeline reaches the compile step.&lt;/p&gt;
&lt;p&gt;Let’s reorder our plugins. I’m going to list the parser first, then the plugins that modify the tree (&lt;code&gt;retext-smartypants&lt;/code&gt;), then those that check the tree (including &lt;code&gt;retext-contractions&lt;/code&gt;), and finally the compiler (&lt;code&gt;retext-stringify&lt;/code&gt;). Again, the parser and compiler can go anywhere in the order, but placing them at the beginning and end reduces confusion.&lt;/p&gt;
&lt;p&gt;Our pipeline should run without warnings!&lt;/p&gt;
&lt;p&gt;Now’s a good time to stop and test out some of the plugins we’re using:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spell something wrong for &lt;code&gt;retext-spell&lt;/code&gt; to catch&lt;/li&gt;
&lt;li&gt;Put an apostrophe in the wrong place (e.g. &lt;code&gt;do&apos;nt&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Miss some diacritics (e.g. &lt;code&gt;a la carte&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;a example&lt;/code&gt; instead of &lt;code&gt;an example&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Repeat a word, &lt;code&gt;The bird in the the bush&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Use gendered language (e.g. &lt;code&gt;postman&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Use profane language (e.g. &lt;code&gt;stupid&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unfortunately, with any large amount of text, a lot of false positives can occur. In most cases, you’ll only want to use a few of these plugins to lint your text. I’m going to remove &lt;code&gt;retext-contractions&lt;/code&gt; and &lt;code&gt;retext-diacritics&lt;/code&gt; at this step.&lt;/p&gt;
&lt;h1 id=&quot;markdown&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#markdown&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Markdown&lt;/h1&gt;
&lt;p&gt;You’ll probably want to use Markdown for any serious work. Markdown allows you to embed links, images, code blocks, and other content into your text.&lt;/p&gt;
&lt;p&gt;This is where the &lt;code&gt;remark&lt;/code&gt; family of plugins can help. We’ll use &lt;code&gt;remark-parse&lt;/code&gt; to parse our Markdown files, and &lt;code&gt;remark-stringify&lt;/code&gt; to convert the tree back to Markdown.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; remark-parse remark-stringify
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;splitting-the-pipeline&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#splitting-the-pipeline&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Splitting the pipeline&lt;/h2&gt;
&lt;p&gt;Right now, our plugins are designed to work with and modify a text syntax tree. If we want to process Markdown, we’ll need some way to convert the Markdown syntax tree into a text syntax tree.&lt;/p&gt;
&lt;p&gt;This is more complicated than it sounds. We can’t use a plugin to replace the Markdown syntax tree with a text one, since we still need to output the Markdown.&lt;/p&gt;
&lt;p&gt;What we &lt;em&gt;can&lt;/em&gt; do is create a separate pipeline that only deals with prose. Let’s move our existing pipeline to a new file, &lt;code&gt;prose.js&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; unified &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unified&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextEnglish &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-english&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextStringify &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-stringify&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextSpell &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-spell&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; dictionary &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dictionary-en&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextEquality &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-equality&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextIndefiniteArticle &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-indefinite-article&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextProfanities &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-profanities&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextRepeatedWords &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-repeated-words&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextSmartypants &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-smartypants&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; retextQuotes &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;retext-quotes&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; fs &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fs/promises&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; personal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./dictionary.txt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Parser&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextEnglish&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Transform prose&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextSmartypants&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Check prose&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextSpell&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    dictionary&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    personal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextEquality&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextIndefiniteArticle&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextProfanities&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextRepeatedWords&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextQuotes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Compiler&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;retextStringify&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; processor&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can import this in our &lt;code&gt;index.js&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; unified &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unified&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; engine &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unified-engine&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; processor &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./prose.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;engine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        processor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./src/**/*.txt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./dist&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;parsing-markdown&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#parsing-markdown&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Parsing Markdown&lt;/h2&gt;
&lt;p&gt;We need some Markdown to parse. I’m using &lt;a href=&quot;https://breq.dev/unified-example/markdown.md&quot;&gt;this&lt;/a&gt; as my source. Save it as &lt;code&gt;src/index.md&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, we’ll make a new pipeline that can parse Markdown, using &lt;code&gt;remark-parse&lt;/code&gt; and &lt;code&gt;remark-stringify&lt;/code&gt;. We’ll also configure our engine to look for &lt;code&gt;.md&lt;/code&gt; files instead of &lt;code&gt;.txt&lt;/code&gt; files.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; unified &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unified&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; engine &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unified-engine&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; remarkParse &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;remark-parse&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; remarkStringify &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;remark-stringify&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkParse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkStringify&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;engine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        processor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./src/**/*.md&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./dist&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we need to actually do the split. If we want to bridge from Markdown to text, from &lt;code&gt;remark&lt;/code&gt; to &lt;code&gt;retext&lt;/code&gt;, we can use &lt;code&gt;remark-retext&lt;/code&gt;!&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; remark-retext
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; remarkRetext &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;remark-retext&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkParse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkRetext&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkStringify&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll notice that this gives an error, though. The &lt;code&gt;remark-retext&lt;/code&gt; plugin is looking for some &lt;code&gt;retext&lt;/code&gt; parser to use. This is where our original prose pipeline comes in.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; prosePipeline &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./prose.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkParse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkRetext&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; prosePipeline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkStringify&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now, the pipeline should run! You’ll probably see some warnings about spelling, showing that the markdown content is getting fed through the spell checker. You might want to update the dictionary before moving on.&lt;/p&gt;
&lt;h2 id=&quot;mutating-markdown&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#mutating-markdown&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Mutating Markdown&lt;/h2&gt;
&lt;p&gt;Look at &lt;code&gt;dist/index.md&lt;/code&gt;. What happened to the smart quotes? In the prose pipeline, we feed our text through &lt;code&gt;retext-smartypants&lt;/code&gt; to convert straight quotes and apostrophes to curly/smart quotes. But that isn’t being reflected in the Markdown output.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Once we split the pipeline, any changes we make to the text syntax tree won’t propagate back to the Markdown tree.&lt;/em&gt; Splitting is a one-way process.&lt;/p&gt;
&lt;p&gt;Thankfully, we can use &lt;code&gt;remark-smartypants&lt;/code&gt; instead of &lt;code&gt;retext-smartypants&lt;/code&gt; to mutate the Markdown tree.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; remark-smartypants
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add it to the Markdown pipeline:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; remarkSmartypants &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;remark-smartypants&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkParse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkSmartypants&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkRetext&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; prosePipeline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkStringify&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, remove &lt;code&gt;retext-smartypants&lt;/code&gt; from the prose pipeline. Run the pipeline again, and you should see smart quotes in the Markdown output. You should also see that &lt;code&gt;retext-quotes&lt;/code&gt; doesn’t complain about quote usage. Since we apply &lt;code&gt;retext-smartypants&lt;/code&gt; before splitting the pipeline, the changes are also reflected in the prose syntax tree.&lt;/p&gt;
&lt;h2 id=&quot;more-markdown-plugins&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#more-markdown-plugins&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;More Markdown Plugins&lt;/h2&gt;
&lt;p&gt;Let’s add a few more plugins to our pipeline.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;remark-slug&lt;/code&gt;: Generate a slug for each heading, letting people link directly to it.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;remark-gfm&lt;/code&gt;: Parse GitHub-style tables.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;remark-frontmatter&lt;/code&gt;: Parse YAML frontmatter.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; remark-slug remark-gfm remark-frontmatter
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; remarkSmartypants &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;remark-smartypants&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; remarkSlug &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;remark-slug&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; remarkGfm &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;remark-gfm&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; remarkFrontmatter &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;remark-frontmatter&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkParse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkFrontmatter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkGfm&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkSlug&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkSmartypants&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkRetext&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; prosePipeline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkStringify&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Try adding some frontmatter, tables, etc. to the Markdown file.&lt;/p&gt;
&lt;pre class=&quot;language-md&quot; data-language=&quot;md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;title: &quot;Hello World&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token table&quot;&gt;&lt;span class=&quot;token table-header-row&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-header important&quot;&gt; this &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-header important&quot;&gt; is    &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token table-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;----&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-----&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token table-data-rows&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-data&quot;&gt; a    &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-data&quot;&gt; table &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Right now, we’re just taking in Markdown and spitting it out. Let’s try actually rendering it to HTML.&lt;/p&gt;
&lt;h1 id=&quot;html&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#html&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;HTML&lt;/h1&gt;
&lt;p&gt;Just as &lt;code&gt;remark&lt;/code&gt; is used for Markdown, &lt;code&gt;rehype&lt;/code&gt; is used for HTML. Instead of writing our Markdown pipeline back to Markdown, let’s transform it to HTML and write that out.&lt;/p&gt;
&lt;p&gt;We’ll need &lt;code&gt;remark-rehype&lt;/code&gt; to transform Markdown to HTML, and &lt;code&gt;rehype-stringify&lt;/code&gt; to write the HTML back to a file. We’ll also use &lt;code&gt;vfile-rename&lt;/code&gt; to rename the &lt;code&gt;.md&lt;/code&gt; files to &lt;code&gt;.html&lt;/code&gt; files.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; remark-rehype rehype-stringify vfile-rename
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can add these to our pipeline. &lt;code&gt;vfile-rename&lt;/code&gt; isn’t a proper plugin, but it only takes a bit of code to make it work.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; remarkRehype &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;remark-rehype&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; rehypeStringify &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rehype-stringify&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; rename &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vfile-rename&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkParse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkFrontmatter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkGfm&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkSlug&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkSmartypants&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkRetext&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; prosePipeline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkRehype&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;rename&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;extname&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.html&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rehypeStringify&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the pipeline again, and you should see &lt;code&gt;index.html&lt;/code&gt; with the output.&lt;/p&gt;
&lt;h2 id=&quot;document-structure&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#document-structure&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Document Structure&lt;/h2&gt;
&lt;p&gt;You might notice that the &lt;code&gt;index.html&lt;/code&gt; doesn’t include &lt;code&gt;&amp;#x3C;head&gt;&lt;/code&gt; and &lt;code&gt;&amp;#x3C;body&gt;&lt;/code&gt;. In order to actually create a complete HTML document, we need to add those. &lt;code&gt;rehype-document&lt;/code&gt; can turn an HTML fragment into a full document.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; rehype-document
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add it to the pipeline, and you should see a complete HTML document.&lt;/p&gt;
&lt;h2 id=&quot;title&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#title&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Title&lt;/h2&gt;
&lt;p&gt;Our &lt;code&gt;index.html&lt;/code&gt; has a &lt;code&gt;&amp;#x3C;title&gt;&lt;/code&gt; tag, but it’s just set to &lt;code&gt;index&lt;/code&gt;. Ideally, we’d want to be able to set the title from the frontmatter.&lt;/p&gt;
&lt;p&gt;There’s no existing plugin to take care of this, but we can write one ourselves. We can extract the title from the frontmatter using &lt;code&gt;remark-extract-frontmatter&lt;/code&gt;, and then use &lt;code&gt;hast-util-select&lt;/code&gt; and &lt;code&gt;hast-util-from-string&lt;/code&gt; to modify the &lt;code&gt;&amp;#x3C;title&gt;&lt;/code&gt; tag.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; hast-util-select hast-util-from-string remark-extract-frontmatter yaml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adding these, our pipeline looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; select &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hast-util-select&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; fromString &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hast-util-from-string&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;YAML&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yaml&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; remarkFrontmatter &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;remark-frontmatter&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkParse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkFrontmatter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkExtractFrontmatter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;YAML&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parse &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkGfm&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkSlug&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkSmartypants&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkRetext&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; prosePipeline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkRehype&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rehypeDocument&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Untitled&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Untitled&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tag &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tree&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tag&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;fromString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tag&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;rename&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;extname&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.html&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rehypeStringify&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Set the &lt;code&gt;title&lt;/code&gt; property in the frontmatter of the markdown, and check that it is updated in the &lt;code&gt;.html&lt;/code&gt; output.&lt;/p&gt;
&lt;h2 id=&quot;formatting&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#formatting&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Formatting&lt;/h2&gt;
&lt;p&gt;Right now, the HTML output isn’t particularly readable. We can add &lt;code&gt;rehype-format&lt;/code&gt; to clean things up. Alternatively, you might want to &lt;code&gt;rehype-minify&lt;/code&gt; to reduce the file size.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; rehype-format
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add this to the pipeline right before the call to &lt;code&gt;.use(rehypeStringify)&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;code&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#code&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Code&lt;/h2&gt;
&lt;p&gt;Add some code to the &lt;code&gt;index.md&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;```js
() =&gt; (tree, file) =&gt; {
  const title = file.data.title || &quot;Untitled&quot;;
  const tag = select(&quot;title&quot;, tree);
  if (tag) {
    fromString(tag, title);
  }
};
```
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The HTML output is fine. The code is printed in a monospace font. However, in most cases, you’ll want to display code with syntax highlighting. The Prism library is a popular choice, and it’s supported in Unified through &lt;code&gt;rehype-prism&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; @mapbox/rehype-prism
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rehypePrism&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This won’t work on its own, however. We need to add the Prism theme to actually apply the highlighting. Thankfully, all we need to do is add the URL to the &lt;code&gt;css&lt;/code&gt; option of &lt;code&gt;rehype-document&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rehypeDocument&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Untitled&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/themes/prism.min.css&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There it is! Language-specific code highlighting has been added to the pipeline.&lt;/p&gt;
&lt;h2 id=&quot;math&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#math&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Math&lt;/h2&gt;
&lt;p&gt;There are a lot of cases where you might want to include math in your Markdown. To accomplish this, math is typically written using LaTeX inside of &lt;code&gt;$&lt;/code&gt; blocks. Here’s what it looks like:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot; data-language=&quot;md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;$$ x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a} $$
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Getting math to display in our pipeline takes two steps. First, when parsing Markdown, we need to add a new plugin to extract the math blocks into syntax tree nodes. Then, we need a plugin to render the LaTeX to HTML once we have our HTML syntax tree.&lt;/p&gt;
&lt;p&gt;There are two major math libraries for the web: MathJAX and KaTeX. We’ll proceed using KaTeX, since it is more lightweight.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; remark-math rehype-katex
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, add these to the pipeline, and add the KaTeX CSS similarly to how we added the Prism theme. The pipeline looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkParse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkFrontmatter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkExtractFrontmatter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;YAML&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parse &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkGfm&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkMath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkSlug&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkSmartypants&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkRetext&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; prosePipeline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remarkRehype&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rehypePrism&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rehypeKatex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rehypeDocument&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Untitled&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rehypeDocument&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Untitled&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/themes/prism.min.css&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.15.1/katex.min.css&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Untitled&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tag &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tree&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tag&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;fromString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tag&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;rename&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;extname&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.html&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rehypeFormat&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rehypeStringify&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both &lt;code&gt;remark-math&lt;/code&gt; and &lt;code&gt;rehype-katex&lt;/code&gt; support both inline and block mode math. Inline mode can be written using a single &lt;code&gt;$&lt;/code&gt; as a delimiter, and block mode uses two &lt;code&gt;$$&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;music&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#music&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Music&lt;/h2&gt;
&lt;p&gt;You also might want to include sheet music notation in your Markdown. The most popular notation for embedding music notation in websites is &lt;a href=&quot;https://abcnotation.com/&quot;&gt;ABC&lt;/a&gt;. There aren’t any working &lt;code&gt;remark&lt;/code&gt; libraries for this, but we can write our own.&lt;/p&gt;
&lt;p&gt;For syntax, let’s use three backticks like a code block, and set the language to &lt;code&gt;abc&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;```abc
X: 1
T: Nokia Tune
M: 3/4
L: 1/8
K: Amaj
| e&apos;d&apos; f2 g2 | c&apos;b d2 e2 | ba c2 e2 | a6 |
```
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can start writing our plugin. Create a new file, &lt;code&gt;music.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;remarkMusic&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; remarkMusic&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Import the plugin and add it to the pipeline in &lt;code&gt;main.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The next step is to select the music nodes in the syntax tree. Let’s start by just &lt;code&gt;inspect&lt;/code&gt;ing the whole tree, to get a sense of what we’re looking for.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inspect &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unist-util-inspect&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;remarkMusic&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; remarkMusic&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;...
└─10 code &quot;X: 1\nT: Nokia Tune\nM: 3/4\nL: 1/8\nK: Amaj\n| e&apos;d&apos; f2 g2 | c&apos;b d2 e2 | ba c2 e2 | a6 |&quot; (42:1-49:4, 1623-1717)
      lang: &quot;abc&quot;
      meta: null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All right, our music is in the syntax tree, in a node with &lt;code&gt;type=&quot;code&quot;&lt;/code&gt; and &lt;code&gt;lang=&quot;abc&quot;&lt;/code&gt;. Let’s start by mapping the &lt;code&gt;code&lt;/code&gt; nodes to &lt;code&gt;abc&lt;/code&gt; nodes.&lt;/p&gt;
&lt;p&gt;To perform this mapping, we can use &lt;code&gt;unist-util-map&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; unist-util-map
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; map &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unist-util-map&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;remarkMusic&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;code&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lang &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you run the pipeline now, you’ll see that the ABC source is now just kind of dropped into the HTML. Unsurprisingly, &lt;code&gt;remark-rehype&lt;/code&gt; has no idea what to do with it.&lt;/p&gt;
&lt;p&gt;If, however, we add a &lt;code&gt;data&lt;/code&gt; field to the &lt;code&gt;abc&lt;/code&gt; nodes we create, we will be able to pass an HTML syntax tree node to &lt;code&gt;remark-rehype&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;remarkMusic&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;code&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lang &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;hName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;div&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;hProperties&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;color: red&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;hChildren&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, we just create a &lt;code&gt;div&lt;/code&gt; for the ABC source, and show it in red.&lt;/p&gt;
&lt;p&gt;However, if we want to actually render ABC to HTML, we’ll need to use a library that works with the DOM. Let’s get things set up beforehand by creating a DOM element and converting it to an AST node.&lt;/p&gt;
&lt;p&gt;Since we’re working with Node, we don’t have access to a global &lt;code&gt;document&lt;/code&gt; object from which to call &lt;code&gt;createElement&lt;/code&gt;. Instead, we can use &lt;code&gt;JSDOM&lt;/code&gt;. We’ll also need &lt;code&gt;hast-util-from-dom&lt;/code&gt; to convert the DOM node to an AST node.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; jsdom hast-util-from-dom
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSDOM&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;jsdom&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; fromDom &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hast-util-from-dom&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;remarkMusic&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;code&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lang &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; document &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JSDOM&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; renderInto &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;div&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        renderInto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerHTML &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        renderInto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;red&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fromDom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;renderInto&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;hName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tagName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;hProperties&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;properties&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;hChildren&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All right! We’ve done almost everything we need, all that’s left is to map some ABC source to an HTML DOM node. Thankfully, considering the popularity of ABC notation, there’s a library for that: &lt;code&gt;abcjs&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; abcjs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we just tell &lt;code&gt;abcjs&lt;/code&gt; to render into our &lt;code&gt;JSDOM&lt;/code&gt; node.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;remarkMusic&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tree&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;code&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lang &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; document &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JSDOM&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; renderInto &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;div&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;ABCJS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;renderAbc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;renderInto&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fromDom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;renderInto&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;hName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tagName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;hProperties&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;properties&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;hChildren&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you’re following along, you might get an error after this step. ABCJS relied in the global &lt;code&gt;window&lt;/code&gt; object, which doesn’t exist in Node, up until &lt;a href=&quot;https://github.com/paulrosen/abcjs/commit/a2f7aa3d1e56129c50bda055ac5b1d70eb99d39d#diff-0396bfd9215f5827170dbea5ff6109b231a55af9bab8c7a2a094aae98da55fc0&quot;&gt;this commit&lt;/a&gt;. At time of writing, it hasn’t made it into a proper release yet. As a workaround, you can install the 6.0.0 beta:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; abcjs@^6.0.0-beta
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And… still doesn’t work. When rendering to a DOM node, ABCJS tries to call &lt;code&gt;document.createElement&lt;/code&gt;, which (obviously) fails. We will need to patch the package manually.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; patch-package
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I added the following patch:&lt;/p&gt;
&lt;pre class=&quot;language-diff&quot; data-language=&quot;diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;diff --git a/node_modules/abcjs/src/write/svg.js b/node_modules/abcjs/src/write/svg.js
index 174602b..fae9221 100644
&lt;span class=&quot;token coord&quot;&gt;--- a/node_modules/abcjs/src/write/svg.js&lt;/span&gt;
&lt;span class=&quot;token coord&quot;&gt;+++ b/node_modules/abcjs/src/write/svg.js&lt;/span&gt;
&lt;span class=&quot;token coord&quot;&gt;@@ -2,6 +2,9 @@&lt;/span&gt;

&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;/*global module */
&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;const JSDOM = require(&quot;jsdom&quot;).JSDOM;
&lt;/span&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;const document = (new JSDOM()).window.document;
&lt;/span&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;var svgNS = &quot;http://www.w3.org/2000/svg&quot;;
&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;function Svg(wrapper) {
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And… there we go! Runs without issue, and transforms our ABC source into beautiful sheet music.&lt;/p&gt;
&lt;h1 id=&quot;pulling-it-all-together&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#pulling-it-all-together&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Pulling it all together&lt;/h1&gt;
&lt;p&gt;Let’s throw a few more styles in, just to make things look a bit nicer.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rehypeDocument&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Untitled&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/themes/prism.min.css&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.15.1/katex.min.css&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.1/css/bootstrap.min.css&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;body { margin: 0 auto !important; max-width: 800px; }&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally, let’s throw some markdown at this! Here’s a snippet that includes basically everything we’re doing:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot; data-language=&quot;md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token front-matter-block&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token front-matter yaml language-yaml&quot;&gt;title: My Awesome Markdown&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token title important&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;#&lt;/span&gt; Hello, World!&lt;/span&gt;

This is a Markdown document. It&apos;s a good test for our pipeline. I hope I &lt;span class=&quot;token strike&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;~~&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;spellled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;~~&lt;/span&gt;&lt;/span&gt; everything right. If someone finds a spelling &lt;span class=&quot;token bold&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;/span&gt;, she should let me &lt;span class=&quot;token url&quot;&gt;[&lt;span class=&quot;token content&quot;&gt;know&lt;/span&gt;](&lt;span class=&quot;token url&quot;&gt;mailto:breq@breq.dev&lt;/span&gt;)&lt;/span&gt;.

&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; This is a list item
&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; This is another list item

&lt;span class=&quot;token list punctuation&quot;&gt;1.&lt;/span&gt; This is a numbered list item
&lt;span class=&quot;token list punctuation&quot;&gt;1.&lt;/span&gt; This is another numbered list item

&lt;span class=&quot;token title important&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;##&lt;/span&gt; Tables&lt;/span&gt;

&lt;span class=&quot;token table&quot;&gt;&lt;span class=&quot;token table-header-row&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-header important&quot;&gt; $x$ &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-header important&quot;&gt; $x^2$ &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token table-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-----&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token table-data-rows&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-data&quot;&gt; 1   &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-data&quot;&gt; 1     &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-data&quot;&gt; 2   &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-data&quot;&gt; 4     &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-data&quot;&gt; 3   &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token table-data&quot;&gt; 9     &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token title important&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;##&lt;/span&gt; Formulas&lt;/span&gt;

$$
x! = \begin{cases}
  x = 0: &amp;#x26; 1 \\
  x &gt; 0: &amp;#x26; x (x - 1)! \\
\end{cases}
$$

$$
x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}
$$

&lt;span class=&quot;token title important&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;##&lt;/span&gt; Code&lt;/span&gt;

&lt;span class=&quot;token code&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;```&lt;/span&gt;&lt;span class=&quot;token code-language&quot;&gt;python&lt;/span&gt;
&lt;span class=&quot;token code-block language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;bisect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  c &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; c
  &lt;span class=&quot;token keyword&quot;&gt;elif&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x3C;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; bisect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; bisect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;```&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token code&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;```&lt;/span&gt;&lt;span class=&quot;token code-language&quot;&gt;jsx&lt;/span&gt;
&lt;span class=&quot;token code-block language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Home&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Hello, world!&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;#x3C;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;```&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token title important&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;##&lt;/span&gt; Music&lt;/span&gt;

&lt;span class=&quot;token code&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;```&lt;/span&gt;&lt;span class=&quot;token code-language&quot;&gt;abc&lt;/span&gt;
&lt;span class=&quot;token code-block language-abc&quot;&gt;X: 1
T: Nokia Tune
M: 3/4
L: 1/8
K: Amaj
| e&apos;d&apos; f2 g2 | c&apos;b d2 e2 | ba c2 e2 | a6 |&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And, let’s render it one last time.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1109&quot; height=&quot;1196&quot; src=&quot;https://breq.dev/_astro/unified-example.CwkjFRWx_Z2rjwCK.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Looks nice! And we’ll check the warnings:&lt;/p&gt;
&lt;pre class=&quot;language-plaintext&quot; data-language=&quot;plaintext&quot;&gt;&lt;code class=&quot;language-plaintext&quot;&gt;./src/index.md &gt; dist/index.html
   8:3-8:11  warning  `spellled` is misspelt; did you mean `spelled`?     spellled  retext-spell
  8:71-8:74  warning  `she` may be insensitive, use `they`, `it` instead  he-she    retext-equality

⚠ 2 warnings
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The pipeline is warning us about both the spelling error and the unnecessary use of gendered language.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#summary&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Summary&lt;/h2&gt;
&lt;p&gt;We’ve built a pipeline that, ultimately, turns Markdown into HTML. But we’ve used the Unified ecosystem to add plenty of other features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use YAML frontmatter to set the page title&lt;/li&gt;
&lt;li&gt;Use Github-Flavored Markdown to render tables, strikethroughs, and other features absent from CommonMark (the commonly used Markdown spec).&lt;/li&gt;
&lt;li&gt;Parse ABC notation and render sheet music&lt;/li&gt;
&lt;li&gt;Add syntax highlighting to code blocks&lt;/li&gt;
&lt;li&gt;Render LaTeX math formulas&lt;/li&gt;
&lt;li&gt;Add slugs to headings to support direct linking&lt;/li&gt;
&lt;li&gt;Convert simple/straight quotes into smart quotes&lt;/li&gt;
&lt;li&gt;Pretty-print the output HTML&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As well as our prose pipeline, which checks the source text for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spelling mistakes, including use of a personal dictionary to ignore certain words&lt;/li&gt;
&lt;li&gt;Potentially insensitive or inconsiderate language (such as gendered pronouns)&lt;/li&gt;
&lt;li&gt;Improper use of “a” versus “an”&lt;/li&gt;
&lt;li&gt;Potentially profane language&lt;/li&gt;
&lt;li&gt;Words that are are improperly repeated&lt;/li&gt;
&lt;li&gt;Mistakes with quote usage&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is quite the feature set! And it goes to show just how broad the Unified ecosystem is. Most of these plugins could be added to the pipeline with just one line of code.&lt;/p&gt;
&lt;p&gt;You can see the final result &lt;a href=&quot;https://github.com/breqdev/unified-example&quot;&gt;in this repo&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;epilogue&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#epilogue&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Epilogue&lt;/h2&gt;
&lt;p&gt;One final note: over the course of writing this, I actually decided to publish the music plugin to NPM. It’s available as &lt;code&gt;remark-abcjs&lt;/code&gt;, and it bundles a patched version of &lt;code&gt;abcjs&lt;/code&gt; to avoid having to patch it yourself. Give it a try!&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; remark-abcjs
&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>LetMeIn</title><link>https://breq.dev/projects/letmein/</link><guid isPermaLink="true">https://breq.dev/projects/letmein/</guid><description>Automate unlocking doors at Northeastern&apos;s IV dorm.</description><pubDate>Sun, 19 Dec 2021 00:00:00 GMT</pubDate><content:encoded>&lt;iframe src=&quot;https://www.youtube.com/embed/zk6Bb-aY_Yo?si=s_ofJ9itNmFts5d6&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; class=&quot;w-full aspect-video rounded-2xl overflow-clip&quot;&gt;&lt;/iframe&gt;
&lt;p class=&quot;caption&quot;&gt;A video of me unlocking my door. Don&apos;t worry, I changed my PIN afterwards.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I wanted to learn how to use &lt;a href=&quot;https://pptr.dev/&quot;&gt;Puppeteer&lt;/a&gt;, since I’ve seen it used in different projects. Puppeteer automates a browser, and it’s used for both automated testing and in various backend applications. For instance, &lt;a href=&quot;https://www.youtube.com/watch?v=bfd8RyAJh6c&quot;&gt;VSinder&lt;/a&gt; used Puppeteer to automate screenshotting code snippets, which famously knocked &lt;a href=&quot;https://carbon.now.sh/&quot;&gt;carbon&lt;/a&gt; offline for while when it was DDOSed. Lore aside, I wanted to get experience with actually using it, since I figured it would likely come in handy at some point.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The code is just a hundred lines of NodeJS. The Puppeteer API makes heavy use of Promises, so I wrote it all using async functions. I’m using &lt;a href=&quot;https://koajs.com/&quot;&gt;Koa&lt;/a&gt; to trigger the process when a HTTP request comes in—it’s like Express, but based on promises instead of callbacks.&lt;/p&gt;
&lt;p&gt;I had a tough time getting a consistent setup to automate. Northeastern uses its own SSO system, plus &lt;a href=&quot;https://duo.com/&quot;&gt;Duo&lt;/a&gt; for two-factor authentication. Annoyingly, these two are seemingly on different expiration timers, making reliable execution difficult.&lt;/p&gt;
&lt;p&gt;I eventually decided to try removing all Northeastern cookies on each invocation and manually stepping through the sign-in process (typing in username and password), which worked well to consistently get through Northeastern’s login step. For Duo, I tried to save cookies between invocations, but ran into unreliable behavior.&lt;/p&gt;
&lt;p&gt;I could have configured Duo to send an SMS to a &lt;a href=&quot;https://www.twilio.com/&quot;&gt;Twilio&lt;/a&gt; phone number and then read that into Puppeteer to enter the 2FA code, but I didn’t want to spend money on this project. Thus, in the final video, I manually clicked “approve” on my phone. Sorry for the deception.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;I mean, it worked? It did unlock my door. That said, it’s so impractical in its current state that I don’t think I could salvage it into something actually useful. A couple key takeaways:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Browsers are made for humans, so they aren’t deterministic.&lt;/strong&gt; Browsers have functionality designed for humans, and even with puppeteer, there are going to be some hiccups when controlling them with automation. Even simple things like waiting for a page to be loaded can be hard—requests happen in an unpredictable order, so waiting for one specific event or request can introduce race conditions. The recommended solution is to wait a predetermined interval after the last web request is closed, which covers &lt;em&gt;most&lt;/em&gt; edge cases but is nonetheless inefficient.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Browsers are &lt;em&gt;huge&lt;/em&gt;, and that leads to a lot of overhead.&lt;/strong&gt; Including a full Chromium instance in a project makes &lt;code&gt;node_modules&lt;/code&gt; massive, and even spinning up a new tab to handle a request takes an appreciable amount of time. This is a setup that could work in a parallelized testing rig, but it’s really an option of last resort for any production use case.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Not everything lends itself to automation.&lt;/strong&gt; In this case, my script needed to wait for many different Northeastern sites to load, and considering the primary web portal I used has seemingly not been touched in a decade, it loaded quite slowly. Even though the process was automated, it wasn’t actually that much faster than doing the steps by hand. This surprised me. I’m used to humans slowing down machines, so I figured I could get a significant speedup by applying automation to the problem, but the human was never the bottleneck in the first place.&lt;/p&gt;</content:encoded></item><item><title>flowspace</title><link>https://breq.dev/projects/flowspace/</link><guid isPermaLink="true">https://breq.dev/projects/flowspace/</guid><description>A social network.</description><pubDate>Thu, 25 Nov 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2952&quot; height=&quot;1670&quot; src=&quot;https://breq.dev/_astro/login.DvRBwRDe_1zClDm.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;The login screen for the flowspace web app.&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;flowspace is a social network website. It has a few basic features, such as direct messaging, friend requests, and public and private posts. Notably, there are two “tiers” of friends—the “wave” tier includes anyone you acknowledge and allow to message you, and the “follow” tier puts that person’s posts into your feed. This outer tier maintains a gate around private messaging, helping to reduce the potential for harassment, but it also doesn’t require someone to be shown every post from someone just to exchange messages.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1868&quot; height=&quot;1284&quot; src=&quot;https://breq.dev/_astro/feed.BsPhr6Bn_Z1tPHbv.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;The primary feed, containing posts of people you follow.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;Honestly, I kind of just wanted to take on a big project to learn and have fun. I definitely didn’t have any hopes of actually building a userbase, considering the (aptly-named) network effect would make it pretty difficult to get anything off the ground. I figured a larger web project like this would involve a lot of interesting architectural decisions and using frameworks and services that I hadn’t used much before.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The service uses a JAMstack architecture, separating the static client app from the dynamic REST API. The API backend is written in NodeJS.&lt;/p&gt;
&lt;p&gt;Handling HTTP requests, middleware, routing, etc. is done with &lt;a href=&quot;https://koajs.com/&quot;&gt;Koa&lt;/a&gt; and many plugins. Koa is a rewrite of the popular Express framework that uses Promises instead of callbacks—this results in much cleaner code in my opinion :)&lt;/p&gt;
&lt;p&gt;The service uses three databases: PostgreSQL, S3 (compatible), and Redis. PostgreSQL is the primary data store, and it stores user profiles, messages, friend requests, posts, and the like. I’m using &lt;a href=&quot;https://www.prisma.io/&quot;&gt;Prisma&lt;/a&gt; to make working with relational data easier. The S3 compatible service stores profile pictures, and it could be used to store message and post attachments as well. Initially, I used a self-hosted &lt;a href=&quot;https://min.io/&quot;&gt;Minio&lt;/a&gt; container for this, but I decided to switch to GCP because of the generous free tier. Finally, Redis is used to handle rate-limiting the API.&lt;/p&gt;
&lt;p&gt;The client app is a static single-page-application built with React and create-react-app. The CSS is all done in &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind&lt;/a&gt;. I’ve deployed it to &lt;a href=&quot;https://pages.cloudflare.com/&quot;&gt;Cloudflare Pages&lt;/a&gt;, again due to their generous free tier.&lt;/p&gt;
&lt;p&gt;Authentication is handled through JWTs stored in localStorage. Upon login, a user will get two tokens: an access token and a refresh token. The refresh token will allow generating a new access token for up to seven days, letting users stay logged in for a while. Tokens are signed using both a secret key and the user’s password hash, ensuring that if a user resets their password, any existing tokens will be automatically invalidated. Password resets and email verification is handled through &lt;a href=&quot;https://sendgrid.com/&quot;&gt;SendGrid&lt;/a&gt;. (Gotta love free tiers, amirite?)&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2432&quot; height=&quot;1484&quot; src=&quot;https://breq.dev/_astro/messages.V4ETfFIY_1uDN9A.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;The messages page.&lt;/p&gt;
&lt;p&gt;In addition to the REST API, a WebSocket “Gateway” endpoint is provided. This allows clients to subscribe to any message channel for updates, and receive any messages that come in on that channel. This is used for real-time direct messaging.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;It works! And while it’s far from complete, the feature set is pretty good. I don’t think I’m going to work more on it, considering it’s first and foremost a learning project for myself and nobody else is using it much.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2292&quot; height=&quot;1354&quot; src=&quot;https://breq.dev/_astro/profile.QD5nUd1E_1VYQcp.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I hadn’t worked with NodeJS in the backend before. Previously, my go-to was always Flask for these kinds of APIs, and I’d been tinkering with &lt;a href=&quot;https://pgjones.gitlab.io/quart/&quot;&gt;Quart&lt;/a&gt; as well (which is a reimplementation of Flask that uses an event loop via Python’s asyncio library). Eventually, I asked myself why I was using this small fork of Flask just for the event loop feature when NodeJS was famous for its event loop and I was already familiar with JavaScript on the client side. I wasn’t sure how long it would take to get up to speed with NodeJS, but I found it pretty easy to learn from a client-side background. (Having Node APIs not match Web APIs was a bit jarring, and I’d like to look into &lt;a href=&quot;https://deno.land/&quot;&gt;Deno&lt;/a&gt; at some point, but it just doesn’t seem like the ecosystem is there yet.)&lt;/p&gt;
&lt;p&gt;Working with a big relational database was also really interesting. The app has different relationship types: For instance, the mapping from users to posts is one-to-many, but the mapping of users to followers/friends is many-to-many. Learning how to express these relationship types in SQL was a surprising challenge for me, considering I hadn’t really worked with data like this before.&lt;/p&gt;
&lt;p&gt;This was also one of the first major sites I built with Tailwind. CSS is something I’m gradually starting to get better at, and I think Tailwind is helping with that. It really helped shift my perspective on CSS from “those weird layout commands” to “nuanced rules to declaratively describe the layout of any element as a function of its size, children, etc.” Miriam Suzanne’s &lt;a href=&quot;https://www.youtube.com/watch?v=aHUtMbJw8iA&quot;&gt;video&lt;/a&gt; on “Why is CSS so weird?” is a really great starting point for this way of thinking, but I think it really takes time and practice to understand how to work within the CSS rules, instead of around them.&lt;/p&gt;
&lt;p&gt;P.S. if you’d like to message me on there, make an account and browse to my profile at &lt;a href=&quot;https://flowspace.breq.dev/u/AtACdumJQAA&quot;&gt;https://flowspace.breq.dev/u/AtACdumJQAA&lt;/a&gt;. I might not get your message unless you ping me elsewhere though, since I never got around to implementing push notifications :)&lt;/p&gt;</content:encoded></item><item><title>Tunneling, Routing, and NATting my way to smuggle IPv6 into Northeastern</title><link>https://breq.dev/2021/09/06/inv6/</link><guid isPermaLink="true">https://breq.dev/2021/09/06/inv6/</guid><description>Northeastern&apos;s dorms have good WiFi... for most people. But I&apos;ll always find a reason to tinker.</description><pubDate>Mon, 06 Sep 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Northeastern’s dorms have good WiFi… for most people. The speed is excellent, the coverage is great, and I have yet to encounter any content blocking. That said, for a tinkerer like me, I had some concerns.&lt;/p&gt;
&lt;h1 id=&quot;client-isolation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#client-isolation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Client Isolation&lt;/h1&gt;
&lt;p&gt;By default, the Northeastern network isolates devices from each other. On the surface, this is a pretty smart thing to do. It’s great that my computer isn’t directly accessible from every other machine on campus.&lt;/p&gt;
&lt;p&gt;That said, it can be quite a headache to work around sometimes. For my &lt;a href=&quot;https://breq.dev/projects/wallmatrix&quot;&gt;wall mounted matrix display&lt;/a&gt; project, when I was at home, I would connect to it by typing &lt;code&gt;http://matrix.local/&lt;/code&gt; into my browser, and &lt;a href=&quot;https://en.wikipedia.org/wiki/Multicast_DNS&quot;&gt;mDNS&lt;/a&gt; would do its thing, connecting me directly to the display.&lt;/p&gt;
&lt;p&gt;Obviously, with client isolation, that wasn’t going to work.&lt;/p&gt;
&lt;p&gt;In a previous post, I wrote about the &lt;a href=&quot;https://breq.dev/2021/02/10/dokku#wireguard&quot;&gt;Wireguard server&lt;/a&gt; that I run for myself. After setting up a Wireguard client on the matrix, I was able to access it from any of my other devices, provided I remember to turn the VPN on.&lt;/p&gt;
&lt;h1 id=&quot;hey-if-i-have-a-vpn-cant-i-run-ipv6-through-it&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#hey-if-i-have-a-vpn-cant-i-run-ipv6-through-it&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Hey, if I have a VPN, can’t I run IPv6 through it?&lt;/h1&gt;
&lt;p&gt;Northeastern’s WiFi doesn’t natively support IPv6. I want to have access to an IPv6 network, so that I can test the IPv6 functionality of my websites.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2332&quot; height=&quot;1279&quot; src=&quot;https://breq.dev/_astro/ipv6-test-neu.BdQ8N5wh_Z1rj7bi.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;Nope. No IPv6 support here.&lt;/p&gt;
&lt;p&gt;So, my next question was: could I route IPv6 through my VPN?&lt;/p&gt;
&lt;h2 id=&quot;getting-ipv6-to-microsoft-azure&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#getting-ipv6-to-microsoft-azure&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Getting IPv6 to Microsoft Azure&lt;/h2&gt;
&lt;p&gt;When I first set up my Dokku machine, IPv6 wasn’t available for VMs. It wasn’t released until &lt;a href=&quot;https://azure.microsoft.com/en-us/updates/ipv6-for-azure-virtual-network-is-now-generally-available-2/&quot;&gt;April 2020&lt;/a&gt;. Ugh.&lt;/p&gt;
&lt;p&gt;This &lt;a href=&quot;https://serverfault.com/questions/1014465/adding-a-public-ipv6-address-to-a-linux-vm-in-azure&quot;&gt;ServerFault answer&lt;/a&gt; helped me figure it out, but basically, I needed to attach a completely new virtual NIC to the machine. Which meant a reboot… which meant downtime… which sucks, but it’s kind of inevitable, since I’m hosting so many services on just one machine. (What can I say—as much as I’d love my own redundant Kubernetes cluster, that stuff’s &lt;em&gt;expensive&lt;/em&gt; to run.)&lt;/p&gt;
&lt;p&gt;After changing the VM settings, NIC settings, VLAN settings, and VLAN subnet settings in the Azure portal, I was finally able to access the IPv6 internet from my Azure VM with its new IPv6 address.&lt;/p&gt;
&lt;p&gt;…its single IPv6 address.&lt;/p&gt;
&lt;p&gt;Yep, instead of assigning a /64 to my machine, Microsoft gave me just a single address.&lt;/p&gt;
&lt;h2 id=&quot;fine-ill-nat-it&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#fine-ill-nat-it&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Fine, I’ll NAT it&lt;/h2&gt;
&lt;p&gt;The whole point of IPv6 was to increase the available address space, removing the need for one-to-many network address translation (NAT). With IPv4, there are only enough IP addresses to give each household or small office a single address to share between all devices. Although larger businesses can get larger blocks, they still need to share. NAT allows this sharing to take place.&lt;/p&gt;
&lt;p&gt;IPv6 was &lt;em&gt;supposed&lt;/em&gt; to remove this requirement entirely. Each subnet &lt;em&gt;should&lt;/em&gt; be a &lt;code&gt;/64&lt;/code&gt;, giving plenty of potential addresses to any connected clients. Then, end-users &lt;em&gt;should&lt;/em&gt; get blocks of &lt;code&gt;/56&lt;/code&gt; so that they can create separate subnets as needed.&lt;/p&gt;
&lt;p&gt;But, unfortunately, we don’t live in that reality, so I had to resort to NAT.&lt;/p&gt;
&lt;h2 id=&quot;docker&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#docker&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Docker&lt;/h2&gt;
&lt;p&gt;Docker can be configured to support IPv6 by editing the &lt;code&gt;/etc/docker/daemon.json&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot; data-language=&quot;json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;ipv6&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;fixed-cidr-v6&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fd16:42d4:7eff::/80&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;fixed-cidr-v6&lt;/code&gt; is &lt;em&gt;supposed&lt;/em&gt; to be the public IPv6 range to assign to Docker containers. However, since I don’t have a range of addresses, I’m using a &lt;a href=&quot;https://en.wikipedia.org/wiki/Unique_local_address&quot;&gt;Unique Local Address&lt;/a&gt;, or ULA, range instead. The &lt;code&gt;42d4:73ff&lt;/code&gt; part was randomly generated to reduce the chance of collision.&lt;/p&gt;
&lt;p&gt;Next, I needed to configure Docker to NAT this ULA range instead of trying to route it directly to the Internet. I found some code (ironically itself distributed as a Docker container) that sets up this NAT: &lt;code&gt;robbertkl&lt;/code&gt;’s &lt;a href=&quot;https://github.com/robbertkl/docker-ipv6nat&quot;&gt;docker-ipv6nat&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I’m aware NAT on IPv6 is almost always a no-go, since the huge number of available addresses removes the need for it. […] I’m in no way “pro IPv6 NAT” in the general case; I’m just “pro working shit”.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;At least the repo is clear about being an ugly hack. It was easy enough to deploy to Dokku with &lt;code&gt;docker-options add deploy --privileged --network host -v /var/run/docker.sock:/var/run/docker.sock:ro -v /lib/modules:/lib/modules:ro --restart=on-failure:10&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;wireguard-server&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#wireguard-server&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Wireguard Server&lt;/h2&gt;
&lt;p&gt;Now, I could access the IPv6 Internet from the Wireguard container itself, but I needed to expose this to the clients. I decided to just add another NAT so that I didn’t interfere with how Docker keeps track of container IPs.&lt;/p&gt;
&lt;p&gt;For this subnet, I chose a ULA prefix of &lt;code&gt;fd16:4252:4551::/64&lt;/code&gt;, because I wanted something memorable, and &lt;code&gt;0x42 0x52 0x45 0x51&lt;/code&gt; is the ASCII for “BREQ”. It might not be RFC compliant, but at least it’s cool.&lt;/p&gt;
&lt;p&gt;I started by adding &lt;code&gt;--sysctl=net.ipv6.conf.all.forwarding=1&lt;/code&gt; to the docker options for the Wireguard container. I then added some additional &lt;code&gt;iptables&lt;/code&gt; rules for the IPv6 traffic. Honestly, I’m no expert with iptables, so I just copied the default &lt;code&gt;iptables&lt;/code&gt; rules but changed the command to &lt;code&gt;ip6tables&lt;/code&gt;.&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Rule&lt;/th&gt;&lt;th&gt;Explanation&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;ip6tables -A FORWARD -i %i -j ACCEPT&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Forward all traffic inbound to the Wireguard interface (&lt;code&gt;wg0&lt;/code&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;ip6tables -A FORWARD -o %i -j ACCEPT&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Forward all outbound traffic from &lt;code&gt;wg0&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Apply many-to-one NAT to traffic exiting the container&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;wireguard-peers&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#wireguard-peers&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Wireguard Peers&lt;/h2&gt;
&lt;p&gt;For every peer on the network, I needed to assign an IPv6 address on the subnet. The server became &lt;code&gt;fd16:4252:4551::1&lt;/code&gt;, my desktop was &lt;code&gt;fd16:4252:4551::2&lt;/code&gt;, and so on. I then added &lt;code&gt;AllowedIPs=::/0&lt;/code&gt; to each peer so that it routed all IPv6 traffic over the VPN.&lt;/p&gt;
&lt;h2 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h2&gt;
&lt;p&gt;It’s a pretty janky IPv6 implementation, especially since it has &lt;em&gt;two layers of NAT&lt;/em&gt; when IPv6 wasn’t meant to be used with NAT at all, but it works!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2344&quot; height=&quot;1302&quot; src=&quot;https://breq.dev/_astro/ipv6-test-wireguard.Dx-MHYFi_2qfL6w.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;As a side effect, after adding an &lt;code&gt;AAAA&lt;/code&gt; record to my DNS provider, all of my API services are now available over IPv6 as well. That means &lt;code&gt;cards.api.breq.dev&lt;/code&gt;, &lt;code&gt;botbuilder.api.breq.dev&lt;/code&gt;, etc, are all dual-stack all the way.&lt;/p&gt;
&lt;p&gt;This was a lot of effort for, well, not a lot of importance. That said, I’m glad that I got to experience some of the issues with upgrading a complex network to IPv6. In many ways, it’s a completely different mindset—under a well-thought-out IPv6 system, you want to make sure each node that &lt;em&gt;might have a subnet behind it at some point&lt;/em&gt; has its own &lt;code&gt;/64&lt;/code&gt; to play with. Services like Docker and cloud providers built out their infrastructure without considering this, and in the process of patching IPv6 support in, they didn’t adhere to these best practices, leading to struggles downstream.&lt;/p&gt;</content:encoded></item><item><title>React Twitter NoTrack</title><link>https://breq.dev/projects/react-twitter-notrack/</link><guid isPermaLink="true">https://breq.dev/projects/react-twitter-notrack/</guid><description>A React component library for embedding Tweets without Twitter&apos;s tracking script.</description><pubDate>Fri, 27 Aug 2021 00:00:00 GMT</pubDate><content:encoded>&lt;tweet id=&quot;1431392146339377152&quot;&gt;
&lt;p&gt;&lt;code&gt;react-twitter-notrack&lt;/code&gt; does exactly what it says on the tin: allow you to embed Tweets as React components without exposing your users to tracking. &lt;del&gt;You can install it from &lt;a href=&quot;https://www.npmjs.com/package/react-twitter-notrack&quot;&gt;npm&lt;/a&gt; with &lt;code&gt;npm i react-twitter-notrack&lt;/code&gt;.&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Unfortunately, things seem to have stopped working, probably as a result of Twitter improving their bot detection or shutting down their API. There were enough tradeoffs inherent in this project that I don’t intend to continue maintaining it. Thus, it is no longer usable.&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Tweet &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;react-twitter-notrack&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;#x3C;&lt;/span&gt;Tweet id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;20&quot;&lt;/span&gt; apiUrl&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://twitter-proxy.breq.workers.dev&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I wanted to be able to embed Tweets on my website, so I looked into React-based Twitter embed libraries. The two that I found, &lt;a href=&quot;https://github.com/andrewsuzuki/react-twitter-widgets&quot;&gt;&lt;code&gt;react-twitter-widgets&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/saurabhnemade/react-twitter-embed&quot;&gt;&lt;code&gt;react-twitter-embed&lt;/code&gt;&lt;/a&gt; operated similarly: they both used the Twitter widgets.&lt;/p&gt;
&lt;p&gt;Twitter has a &lt;a href=&quot;https://developer.twitter.com/en/docs/twitter-for-websites/javascript-api/guides/set-up-twitter-for-websites&quot;&gt;system of widgets&lt;/a&gt; that can be embedded on a website. They work by using a JavaScript library to dynamically include iFrames onto the page. I’m not a huge fan of this approach, for a couple reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The iFrames tend to load in after everything else on the page, leading to a huge layout jump.&lt;/li&gt;
&lt;li&gt;Embedding iFrames uses more resources.&lt;/li&gt;
&lt;li&gt;Users who use tracker-blockers, like Firefox Enhanced Tracking Protection, might not see the Tweet at all.&lt;/li&gt;
&lt;li&gt;Allowing Twitter to execute JavaScript on my webpage exposes my users to tracking without their consent.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I decided to try making my own Twitter embed, built as a pure React component without any imperative DOM manipulation.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;I started building out this project using &lt;a href=&quot;https://storybook.js.org/&quot;&gt;Storybook&lt;/a&gt;. I had wanted to try out Storybook for a while by now. I really enjoyed how quickly it allowed me to iterate.&lt;/p&gt;
&lt;p&gt;Using an official Twitter widget as a model, I tried to emulate the design as best I could. I built out this mockup using &lt;a href=&quot;https://styled-components.com/&quot;&gt;&lt;code&gt;styled-components&lt;/code&gt;&lt;/a&gt;, which was another first for me. I think that &lt;code&gt;styled-components&lt;/code&gt; was a good fit for this project—it’s a lot lighter-weight than Tailwind, and there was no need for me to stick to a broader design system like my website as a whole. That said, for more ambitious projects, I’ll probably stick with Tailwind to keep things looking cohesive.&lt;/p&gt;
&lt;p&gt;Then came the data fetching. I noticed that the official Twitter embed was sending a request to &lt;code&gt;cdn.syndication.twimg.com&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;601&quot; height=&quot;490&quot; src=&quot;https://breq.dev/_astro/network-request.WClZVE2K_1AsMkA.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;…and getting back a response with info about the tweet:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;599&quot; height=&quot;619&quot; src=&quot;https://breq.dev/_astro/response.CA0y4j1Q_Z68pBX.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;So, just call this endpoint from the React component and we’re good, right?&lt;/p&gt;
&lt;p&gt;…nope. Twitter uses &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS&quot;&gt;CORS&lt;/a&gt; to only allow &lt;code&gt;platform.twitter.com&lt;/code&gt; to access this endpoint. For the official embed, this isn’t an issue, since the iFrame is loaded from that origin. But for our pirate embed, we’ll need to find another way.&lt;/p&gt;
&lt;p&gt;I ended up building a proxy with Cloudflare Workers to spoof the &lt;code&gt;Origin&lt;/code&gt; header to Twitter and send back a permissive &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; to the client. This is pretty much the same approach I used for GenReGen’s &lt;a href=&quot;https://breq.dev/projects/genregen#technical-description&quot;&gt;Pastebin proxy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With that out of the way, I just had to fetch the data from the proxy. I used trusty old &lt;a href=&quot;https://swr.vercel.app/&quot;&gt;useSWR&lt;/a&gt; to get the job done.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;This library doesn’t produce embeds with as much polish as the official Twitter ones — they don’t show the original tweet when embedding a reply or quote tweet, and they don’t show more than one image at a time for now. But overall, I think this resulted in something usable and more performant than the official embed. For a while, I used it for all the Twitter embeds on this website, and it checked all the boxes. I don’t know if anyone else ever found it useful, but I’m still happy that I shared my work on NPM.&lt;/p&gt;&lt;/tweet&gt;</content:encoded></item><item><title>Building My Online Presence</title><link>https://breq.dev/2021/08/26/website/</link><guid isPermaLink="true">https://breq.dev/2021/08/26/website/</guid><description>My personal website, through the ages.</description><pubDate>Thu, 26 Aug 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve always wanted to keep a personal website up and running. In part, that’s because I want to make sure everything I work on has a home; hopefully, people can use my project writeups for inspiration or advice on their own creative endeavours. It’s also partly because I want a place to share my thoughts. The main reason, however, is to keep a centralized directory of who I am.&lt;/p&gt;
&lt;p&gt;For much of my life, my identity has been, for lack of a better word, unstable. This is an outlet for me to keep track of what makes me who I am. It’s a place where I can post things that I’m proud of.&lt;/p&gt;
&lt;p&gt;(Funnily enough, it was this period of questioning that led me to pick the name &lt;code&gt;breq.dev&lt;/code&gt;. At the time, I knew I was deeply uncomfortable with my name, but I didn’t yet understand why. When it came time to pick a domain name, I ended up scrolling through the &lt;a href=&quot;https://en.wikipedia.org/wiki/Atmel_AVR_instruction_set&quot;&gt;AVR instruction set&lt;/a&gt; until I found something somewhat pronounceable, and it’s kinda stuck!)&lt;/p&gt;
&lt;p&gt;With that out of the way, here’s the journey my online presence took from both a technical and personal perspective.&lt;/p&gt;
&lt;h1 id=&quot;my-first-html&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#my-first-html&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;My First HTML&lt;/h1&gt;
&lt;p&gt;The first HTML file I ever hosted publicly was published on August 19, 2015. Here’s that fateful commit:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1046&quot; height=&quot;541&quot; src=&quot;https://breq.dev/_astro/first-commit.Bwjz3cA8_ucaRu.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;As a 12-year-old with no money, it’s no surprise I turned to &lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt; for hosting. And, as you can tell from the commit, I hadn’t exactly figured out code indentation yet.&lt;/p&gt;
&lt;p&gt;Over the years, this site turned into a disorganized collection of random &lt;code&gt;.html&lt;/code&gt; files I happened to experiment with. This included &lt;a href=&quot;https://breq.dev/projects/gemini&quot;&gt;Gemini&lt;/a&gt; and &lt;a href=&quot;https://breq.dev/projects/bounce&quot;&gt;Bounce&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;the-paper-feed&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-paper-feed&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Paper Feed&lt;/h1&gt;
&lt;p&gt;In around 2019, I started to take this website idea a bit more seriously. I started what I called the Paper Feed — a single-page site where I would post some thoughts every couple weeks. Here’s the only surviving screenshot (which is probably for the best):&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;793&quot; height=&quot;480&quot; src=&quot;https://breq.dev/_astro/paper-feed.CSqs4Pui_7Kuad.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;It wasn’t much — just a basic CSS theme. But it was mine, and it was the first time I started to see my online presence as something I should curate and something that people might be interested in.&lt;/p&gt;
&lt;h1 id=&quot;jekyll-and-the-beginnings-of-the-project-log&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#jekyll-and-the-beginnings-of-the-project-log&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Jekyll, and the beginnings of the project log&lt;/h1&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;830&quot; height=&quot;486&quot; src=&quot;https://breq.dev/_astro/jekyll-initial.DLzZZZQh_Z1dV4gj.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Ouch. The lack of contrast hurts my eyes.&lt;/p&gt;
&lt;p&gt;You can spot a few cool things in this revision though:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No more deadnames to remove! Yay!&lt;/li&gt;
&lt;li&gt;My logo — the cube with the half adder — makes its first appearance.&lt;/li&gt;
&lt;li&gt;“Welcome to my little patch of internet” is still my website slogan to this day.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It didn’t take long for me to start throwing more interesting stuff on that site, however:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1046&quot; height=&quot;827&quot; src=&quot;https://breq.dev/_astro/jekyll-real-initial.DfqrHv3e_ZJKk9t.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I started to really use CSS for things at this point, as you can tell. This page was built with Bootstrap as a base. I also started to make use of Jekyll’s layout features to reduce duplication across pages, and the data features to define the navbar with YAML.&lt;/p&gt;
&lt;p&gt;The site had two pages: an “about” page listing my contact info, and a “projects” page containing writeups for all of the projects. Usability wasn’t great — the project page was a giant wall of text — but those initial descriptions grew into the writeups I currently have on my site.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1115&quot; height=&quot;1393&quot; src=&quot;https://breq.dev/_astro/parallax-bg.Ccpu2scF_Xef1K.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Next came the wireframe logo. Projects started to go into their own pages as well. Things were really starting to take shape.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1115&quot; height=&quot;1393&quot; src=&quot;https://breq.dev/_astro/autoplay-videos.B2lsn4Pn_QsB7.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I added autoplaying videos to bring my projects front and center.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1115&quot; height=&quot;900&quot; src=&quot;https://breq.dev/_astro/pink-and-blue.hBxHE4Yq_Z1G6BvI.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;As I started to accumulate more projects, I switched to a tile-based layout to fit more on the homepage.&lt;/p&gt;
&lt;h1 id=&quot;spinning-off-jekyll-theme-breq&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#spinning-off-jekyll-theme-breq&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Spinning off &lt;code&gt;jekyll-theme-breq&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;After this, I decided to spin off a Jekyll theme with my navbar style into a separate package. I’m still using this theme for my &lt;a href=&quot;https://emoji.breq.dev/&quot;&gt;emoji keyboard&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1031&quot; height=&quot;830&quot; src=&quot;https://breq.dev/_astro/rot-logo.gef4cND6_2pP8YP.webp&quot; srcset=&quot;&quot;&gt; &lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1031&quot; height=&quot;830&quot; src=&quot;https://breq.dev/_astro/new-tiles.DehZAEr5_f4sym.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Not much has changed really here — I’ve switched to a light theme which is easier on the eyes, and I’ve added a big autoplaying video to the homepage. And, finally, I put the name “Brooke” in big letters! I gotta be honest, that felt good.&lt;/p&gt;
&lt;h1 id=&quot;gatsby-and-false-starts&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#gatsby-and-false-starts&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Gatsby, and false starts&lt;/h1&gt;
&lt;p&gt;At this point, however, I was starting to feel like I’d reached the limits of what Jekyll could comfortably handle. Project information was split up between each project file and the YAML data files, and splitting off my theme into its own project hadn’t made things simpler like I’d hoped.&lt;/p&gt;
&lt;p&gt;I decided to make the switch to Gatsby. It was based on React, which I had heard about but wasn’t too comfortable with initially. I was, however, impressed by its claims of legendary performance.&lt;/p&gt;
&lt;p&gt;It took me a couple tries to make the switch. Gatsby’s data layer took me forever to get used to. Thinking of each resource as a “node” and understanding how each step in the pipeline would process that node was a big shift in how I thought about content and data.&lt;/p&gt;
&lt;p&gt;On my third or so attempt to learn Gatsby, I ended up with something feature-complete and decided to make the switch. And I’m glad I did — I’m starting to really love React, and the image optimization, data prefetching, and simple hot reload that Gatsby brings to the table have made my final site much easier to develop and interact with.&lt;/p&gt;
&lt;p&gt;I also switched to Tailwind for CSS around this time, and found it to be the perfect styling system for working with React components. Having CSS in a separate file, even with CSS modules, was something I always tried to find a way around. Tailwind, on the other hand, helps me stay organized.&lt;/p&gt;
&lt;h1 id=&quot;where-we-are-now&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#where-we-are-now&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Where we are now&lt;/h1&gt;
&lt;p&gt;I think I’ve settled on a model that works, both in terms of my technology stack and in terms of the role this website serves in my life.&lt;/p&gt;
&lt;p&gt;Preserving my work is important to me — even if nobody else ever reads it, I want to have this here as a resource for myself, about myself, to remind me of who I am.&lt;/p&gt;</content:encoded></item><item><title>Bounce Homepage</title><link>https://breq.dev/projects/bounce/</link><guid isPermaLink="true">https://breq.dev/projects/bounce/</guid><description>A simple homepage for my school laptop.</description><pubDate>Thu, 26 Aug 2021 00:00:00 GMT</pubDate><content:encoded>&lt;div class=&quot;text-center my-4 text-2xl&quot;&gt;View the &lt;a href=&quot;https://breq.dev/apps/bounce.html&quot;&gt;live demo&lt;/a&gt;!&lt;/div&gt;
&lt;p&gt;Bounce was a homepage I made to make my school laptop just a bit more unique from everyone else’s. It features three bouncing circles. Pressing &lt;kbd&gt;space&lt;/kbd&gt; adds circles to the screen, while pressing &lt;kbd&gt;c&lt;/kbd&gt; takes them away.&lt;/p&gt;
&lt;p&gt;I wanted to have fun and make something unique, and in the end, I was really proud of the result! It was cool to take an open-ended idea like “make a more interesting web browser home page” and brainstorm different implementations.&lt;/p&gt;</content:encoded></item><item><title>Gemini</title><link>https://breq.dev/projects/gemini/</link><guid isPermaLink="true">https://breq.dev/projects/gemini/</guid><description>A canvas-based web game.</description><pubDate>Thu, 26 Aug 2021 00:00:00 GMT</pubDate><content:encoded>&lt;div class=&quot;text-center my-4 text-2xl&quot;&gt;View the &lt;a href=&quot;https://breq.dev/apps/gemini.html&quot;&gt;live demo&lt;/a&gt;!&lt;/div&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;Gemini was a simple canvas game I built. The player had to guide the black circle towards the white circle using the arrow keys, but the black circle is pushed around by a randomly changing current.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I wanted to make a simple game in order to learn JavaScript. I worked on this during a summer camp at MSSM, in an introductory programming course.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;Gemini was certainly a product of 2016. It was built with jQuery. The two balls are represented as JavaScript objects, but they’re created using the prototype interface, as ES2015 classes weren’t widely supported or documented at the time.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;It was something I could show my friends, and it was something that helped me learn JavaScript, so I’d call it a success! That said, I’m glad my code quality has improved since I wrote this… :)&lt;/p&gt;</content:encoded></item><item><title>GenReGen</title><link>https://breq.dev/projects/genregen/</link><guid isPermaLink="true">https://breq.dev/projects/genregen/</guid><description>A mashup generator.</description><pubDate>Thu, 26 Aug 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1031&quot; height=&quot;830&quot; src=&quot;https://breq.dev/_astro/genregen.BVx7mqhI_X7yXT.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;“Genre Gen”-erator? “Gen”-erate “Regen”-erate? Regardless of what the name means, this is a program that’ll take in a list of items and spit out randomized mashups.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I received an email from &lt;a href=&quot;https://deadline.com/2020/07/gerard-butler-screenwriter-mitchell-lafortune-signs-with-apa-1203000985/&quot;&gt;my uncle Mitch&lt;/a&gt; asking for a random genre mashup generator to help with coming up with ideas for movies. I wanted to make something stable, that would continue working even without my involvement.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The brief was simple enough — just pull two random items from a list. I didn’t even use a framework for this; it’s all done with imperative DOM manipulation. Since it’s just a few static files, I’m hosting it on Cloudflare Pages. Implementing a few other features, like going backwards through the list of generated mashups, was also pretty trivial.&lt;/p&gt;
&lt;p&gt;What complicated things a bit was Mitch’s request that the list be editable. I didn’t want to manage an authentication system to restrict access to a central list, since that would require hosting and a custom backend solution (which is more expensive to run, more difficult to implement, and more work to maintain).&lt;/p&gt;
&lt;p&gt;The hack that I settled on was using Pastebin as a source. Anyone can upload a list of things to Pastebin, then put the URL of that list in the “source list” box to generate random pairings from that list. The JavaScript will detect changes to this field and pull the list from Pastebin, using it for all subsequent mashups. It’ll even save the most recent URL in the browser &lt;code&gt;localStorage&lt;/code&gt;, so that the user doesn’t have to keep re-pasting it.&lt;/p&gt;
&lt;p&gt;Of course, there was another hurdle: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS&quot;&gt;CORS&lt;/a&gt;. Yes, it helps keep us safe on the web, yes, it protects private data and intellectual property rights… but in this case, it was just another issue to work around.&lt;/p&gt;
&lt;p&gt;Oh, but Pastebin adds the &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; header, right…?&lt;/p&gt;
&lt;tweet id=&quot;916213585960947712&quot;&gt;
&lt;p&gt;Ah, capitalism strikes again.&lt;/p&gt;
&lt;p&gt;To solve this, I ended up using &lt;a href=&quot;https://workers.dev/&quot;&gt;Cloudflare Workers&lt;/a&gt; to make a simple proxy that adds CORS headers on top of the Pastebin API.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot; data-language=&quot;js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fetch&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;respondWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;handleRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stack&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; incomingOrigin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Origin&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;(breq\.dev|genregen\.pages\.dev)&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;incomingOrigin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Origin Not Allowed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;403&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; paste &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchParams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;paste&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://pastebin.com/raw/&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; paste&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Origin&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pastebin.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Access-Control-Allow-Origin&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; incomingOrigin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Vary&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Origin&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;Honestly, it does everything I had hoped it would. The Pastebin solution is a bit janky, but in the end, I’m glad I got everything working without any recurring costs or complex backends to maintain.&lt;/p&gt;&lt;/tweet&gt;</content:encoded></item><item><title>Wall Matrix</title><link>https://breq.dev/projects/wallmatrix/</link><guid isPermaLink="true">https://breq.dev/projects/wallmatrix/</guid><description>A small, flashy display I made to hang on the wall.</description><pubDate>Thu, 26 Aug 2021 00:00:00 GMT</pubDate><content:encoded>&lt;iframe src=&quot;https://www.youtube.com/embed/o5zavmZU38s?si=s_ofJ9itNmFts5d6&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; class=&quot;w-full aspect-video rounded-2xl overflow-clip&quot;&gt;&lt;/iframe&gt;
&lt;p class=&quot;caption&quot;&gt;A video of me doing the final assembly of the display.&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;This is a sign that I built that hangs on the wall and shows information from the Internet.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I bought an &lt;a href=&quot;https://www.adafruit.com/product/420&quot;&gt;LED matrix panel&lt;/a&gt; from Adafruit years ago, and I’ve tried to use it to display things in the past, but I had never figured out a good way to mount it. Around the time I built this project, I had just gotten some threaded inserts for use with 3D printing, and I figured making a case for this display would be a good project to use them for.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;h2 id=&quot;hardware&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#hardware&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Hardware&lt;/h2&gt;
&lt;p&gt;My 3D printer can only do up to 120mm in each dimension, so I needed to split the print up into two parts. I used threaded inserts to join these together, and I did the split slightly off-center so that the mounting hole would be stronger.&lt;/p&gt;
&lt;p&gt;To attach the Raspberry Pi, I had hoped to screw it into the threaded inserts as well, but the RPi’s mounting holes are M2.5 while the inserts I got are M3. I ended up making pegs that fit into the Pi’s mounting holes, and a “seat belt” to hold the Pi against them. This solution was surprisingly sturdy.&lt;/p&gt;
&lt;p&gt;Finally, it came time to connect the display to the Pi. While Adafruit sells a &lt;a href=&quot;https://www.adafruit.com/product/2345&quot;&gt;HAT&lt;/a&gt; to make the connections easy, it &lt;a href=&quot;https://github.com/hzeller/rpi-rgb-led-matrix/blob/master/wiring.md#alternative-hardware-mappings&quot;&gt;doesn’t use the default mapping&lt;/a&gt; for the library I had planned on using. I decided to build my own on a &lt;a href=&quot;https://www.adafruit.com/product/2310&quot;&gt;Perma-Proto&lt;/a&gt;, and to leave room in case I wanted to add other matrix strands or devices in the future.&lt;/p&gt;
&lt;h2 id=&quot;software&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#software&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Software&lt;/h2&gt;
&lt;p&gt;The matrix is driven using &lt;a href=&quot;https://github.com/hzeller/rpi-rgb-led-matrix&quot;&gt;hzeller’s rpi-rgb-led-matrix&lt;/a&gt; library. Specifically, I’m making use of the Python bindings.&lt;/p&gt;
&lt;p&gt;The code is split into four major sections:&lt;/p&gt;
&lt;h3 id=&quot;the-driver&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-driver&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Driver&lt;/h3&gt;
&lt;p&gt;The driver exposes an interface allowing other components in the stack to send an image to the matrix. I wrote two drivers: a “real” driver that shows the image on the matrix, and a “fake” driver that draws the image in a TkInter window. This allows me to test out new sources, images, and designs on my own computer before deploying them to the Pi.&lt;/p&gt;
&lt;h3 id=&quot;the-server&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-server&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Server&lt;/h3&gt;
&lt;p&gt;The server is written in Flask, but I’ve turned off the threading abilities so that the driver can remain a singleton. (Normally, this is a bad idea, but I’ve decided that it’s fine, since this is an embedded device anyway.) It loads in any available sources, retrieves an image from the current source, and handles incoming requests to change the active source or interrupt the source with a scrolling message.&lt;/p&gt;
&lt;p&gt;The server and driver communicate over a message queue. The server will send a &lt;code&gt;SOURCE_CHANGED&lt;/code&gt; message when the user chooses a different source, and it will send a &lt;code&gt;FLASH_MESSAGE&lt;/code&gt; message when the user submits a message to show.&lt;/p&gt;
&lt;h3 id=&quot;the-sources&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-sources&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Sources&lt;/h3&gt;
&lt;p&gt;I’ve written four basic sources so far.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ColorBars&lt;/strong&gt;: Shows the basic color bar test image on the screen.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;699&quot; height=&quot;700&quot; src=&quot;https://breq.dev/_astro/weather.kApdxApI_15oMmf.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Weather&lt;/strong&gt;: Shows the current time, temperature, and weather on the screen, using the OpenWeatherMap API.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;726&quot; height=&quot;726&quot; src=&quot;https://breq.dev/_astro/crypto.C78Tic3e_Z2dChp9.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Crypto&lt;/strong&gt;: Shows the current price and 24-hour percent change of a cryptocurrency (defaults to ETH), using the CoinMarketCap API.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;713&quot; height=&quot;713&quot; src=&quot;https://breq.dev/_astro/mbta.BxuVeZaS_Rbj7V.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MBTA&lt;/strong&gt;: Shows the next northbound Green Line and Orange Line trains passing through the Northeastern University campus, using the MBTA’s official API.&lt;/p&gt;
&lt;p&gt;These sources inherit from a base &lt;code&gt;Source&lt;/code&gt; class, which has some data caching logic built in. By default, APIs are only called once every 60 seconds, but the Crypto source caches data for 5 minutes due to the more restrictive API license.&lt;/p&gt;
&lt;h3 id=&quot;the-client&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-client&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Client&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;750&quot; height=&quot;799&quot; src=&quot;https://breq.dev/_astro/client.DFplRRQG_1aIEO7.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The client is written in static HTML/CSS/JS. It features a dropdown menu to change the source and a textbox to input messages to flash. I designed it to be usable on a phone, since I figured that’s how most people would like to control the sign.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;The finished result looks a lot more polished than I was expecting! I had a lot of bugs to work out at first — it turns out there are a lot more edge cases than I thought there would be when it comes to displaying time — but it’s been quite reliable.&lt;/p&gt;</content:encoded></item><item><title>PocKey - RP2040, SH1107, and lessons from a failed project</title><link>https://breq.dev/2021/08/21/pockey/</link><guid isPermaLink="true">https://breq.dev/2021/08/21/pockey/</guid><description>I invested countless hours into this project and I have nothing to show for it but a story.</description><pubDate>Sat, 21 Aug 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is a story about a project I made that failed. By “failed,” I don’t mean just something that didn’t perform well or didn’t meet my expectations. What I mean is, I invested countless hours into this project and I have nothing to show for it. That is, nothing but a story.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Did you come here looking for LVGL tips? Skip to &lt;a href=&quot;#sh1107&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Looking for source code? Here’s my &lt;a href=&quot;https://github.com/breqdev/pockey&quot;&gt;CircuitPython work&lt;/a&gt; and my &lt;a href=&quot;https://github.com/breqdev/pocketdeck&quot;&gt;RP2040 SDK work&lt;/a&gt; on GitHub.&lt;/em&gt;&lt;/p&gt;
&lt;h1 id=&quot;the-premise&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-premise&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Premise&lt;/h1&gt;
&lt;p&gt;For years, I had been wanting to build a musical instrument using Adafruit’s &lt;a href=&quot;https://www.adafruit.com/product/3954&quot;&gt;NeoTrellis&lt;/a&gt; kit. This board, combined with a silicone keypad, creates a surface of 16 buttons with individual RGB lighting.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;970&quot; height=&quot;727&quot; src=&quot;https://breq.dev/_astro/neotrellis.B6mgVTR4_ssVWf.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;Picture from Adafruit demonstrating the NeoTrellis system.&lt;/p&gt;
&lt;p&gt;This arrangement reminded me of the &lt;a href=&quot;https://novationmusic.com/en/launch/launchpad-x&quot;&gt;Novation Launchpad&lt;/a&gt; with its arrangement of colorful buttons. I decided to try to make a similar USB MIDI controller.&lt;/p&gt;
&lt;p&gt;I then had to decide which microcontroller to pick. At the time, the &lt;a href=&quot;https://www.raspberrypi.org/documentation/rp2040/getting-started/&quot;&gt;RP2040&lt;/a&gt; from Raspberry Pi was new, and I wanted to experiment with it. I eventually went with Adafruit’s &lt;a href=&quot;https://www.adafruit.com/product/4884&quot;&gt;RP2040 Feather&lt;/a&gt;, and I threw in an &lt;a href=&quot;https://www.adafruit.com/product/4650&quot;&gt;SH1107-based display add-on&lt;/a&gt; as an afterthought.&lt;/p&gt;
&lt;p&gt;I called it “PocKey,” both because it was a pocket keypad/keyboard, and because I like Pocky.&lt;/p&gt;
&lt;h1 id=&quot;physical-construction&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#physical-construction&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Physical Construction&lt;/h1&gt;
&lt;p&gt;This, surprisingly, went pretty well. I mocked up a basic clamshell design in SketchUp…&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1123&quot; height=&quot;671&quot; src=&quot;https://breq.dev/_astro/case-cad.B8BWQPic_Q5lI6.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Everything pretty much fit on the first try, except for a few issues with the Feather mounting holes:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;996&quot; height=&quot;1328&quot; src=&quot;https://breq.dev/_astro/case-printed.dqXz8Kjl_tpH4z.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;Sorry about the horrible picture quality, I got a new phone with a better camera about a month after I took this picture...&lt;/p&gt;
&lt;p&gt;With that out of the way, I turned to software.&lt;/p&gt;
&lt;h1 id=&quot;circuitpython&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#circuitpython&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;CircuitPython&lt;/h1&gt;
&lt;p&gt;I hadn’t done much with &lt;a href=&quot;https://micropython.org/&quot;&gt;MicroPython&lt;/a&gt; before (or with &lt;a href=&quot;https://circuitpython.org/&quot;&gt;CircuitPython&lt;/a&gt;, Adafruit’s fork). I had used it a bit with the ESP8266, but hadn’t had much success.&lt;/p&gt;
&lt;p&gt;Thankfully, I had a much better experience this time around, at least at first. Adafruit publishes CircuitPython libraries for pretty much all of their boards, which really made throwing things together a lot easier. With the hardware interfacing abstracted away, I just had to focus on the application logic.&lt;/p&gt;
&lt;h2 id=&quot;tangent-hot-reload&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#tangent-hot-reload&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Tangent: Hot-Reload&lt;/h2&gt;
&lt;p&gt;Here is where the scope of the project started to expand a bit. I decided it would be interesting to have multiple loadable “apps” on the device, such as a macro keyboard, MIDI controller, or even some simple games. While I was building out an app loader, CircuitPython’s &lt;a href=&quot;https://learn.adafruit.com/welcome-to-circuitpython/creating-and-editing-code#editing-code-2977443-18&quot;&gt;hot reload&lt;/a&gt; functionality started to get in the way.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To edit code, open the code.py file on your CIRCUITPY drive into your editor. Make the desired changes to your code. Save the file. That’s it! Your code changes are run as soon as the file is done saving.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While this functionality makes things easy for most projects, it started to get in my way, since changing a single app would require reloading the entire project, losing any persistent state. I decided to work on my own “hot reload” functionality.&lt;/p&gt;
&lt;p&gt;This supporting code ended up turning into a full-on operating system, handling display updates, button interrupts, and a ton of other functionality. But it did end up making apps easier to write. Here’s a basic one I wrote that provides macro keys — two keys on the main board, and an additional one on the display:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; usb_hid
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; adafruit_hid&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keyboard &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Keyboard
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; adafruit_hid&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keyboard_layout_us &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; KeyboardLayoutUS
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; adafruit_hid&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keycode &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Keycode

&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; pockey&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;app &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; App

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;KeyboardApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;App&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pockey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token builtin&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__init__&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keyboard &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Keyboard&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;usb_hid&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;devices&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layout &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; KeyboardLayoutUS&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keyboard&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mapping &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello World!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://google.com/\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&apos;A&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello hello!&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pockey&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;enabled &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;

        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pockey&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Keyboard Demo&quot;&lt;/span&gt;

        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pockey&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;trellis&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pockey&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;trellis&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle_button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; edge&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; edge &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pockey&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PRESSED&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; number &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mapping&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;write&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mapping&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mainloop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;pass&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;teardown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keyboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;release_all&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; KeyboardApp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So far, things were going great! I did notice that the apps were starting to feel a bit sluggish, however…&lt;/p&gt;
&lt;h2 id=&quot;unreasonable-defaults-auto-writes-and-auto-refreshes&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#unreasonable-defaults-auto-writes-and-auto-refreshes&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Unreasonable Defaults: Auto-Writes and Auto-Refreshes&lt;/h2&gt;
&lt;p&gt;The CircuitPython documentation makes it clear that CircuitPython is intended primarily as an educational tool.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The easiest way to program microcontrollers: CircuitPython is a programming language designed to simplify experimenting and learning to code on low-cost microcontroller boards.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Going into this, I wasn’t expecting CircuitPython to be the highest-performance library out there by any means. That said, I was still surprised with the amount of tweaking I needed to do just to get a halfway acceptable level of performance.&lt;/p&gt;
&lt;p&gt;CircuitPython, by default, will immediately write any updates to NeoPixel strands or graphical displays. Admittedly, this makes things a bit easier to get started with—when I was just starting out with embedded software, I always forgot to call &lt;code&gt;display.show()&lt;/code&gt; to push updates to the screen, and I would sit there and wonder why nothing was happening.&lt;/p&gt;
&lt;p&gt;That said, this approach makes performance much worse. The reason for this is straightforward: generally, multiple things are drawn on the screen at a time, and doing a refresh for each one of these things will be a lot slower than just doing a single refresh at the end.&lt;/p&gt;
&lt;p&gt;Fortunately, this behavior was simple enough to disable by setting &lt;code&gt;trellis.pixels.auto_write = False&lt;/code&gt; and passing &lt;code&gt;auto_refresh=False&lt;/code&gt; to the &lt;code&gt;adafruit_displayio_sh1107.SH1107()&lt;/code&gt; constructor.&lt;/p&gt;
&lt;h2 id=&quot;update-diffing-and-other-trickery&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#update-diffing-and-other-trickery&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Update diffing and other trickery&lt;/h2&gt;
&lt;p&gt;At this point, I was still having trouble with performance. I decided to try to reduce unnecessary updates further by calculating the difference between the new and old state. Here’s how I did that with the NeoPixels:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;trellis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sync&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    virtual &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;virtual
    actual &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;actual

    dirty &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;False&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; pixel &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; virtual&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;pixel&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; actual&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;pixel&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;trellis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pixels&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;pixel&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; virtual&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;pixel&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
            actual&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;pixel&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; virtual&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;pixel&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
            dirty &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; dirty&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;trellis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pixels&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;show&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I did some similar processing of the display output by tracking the text that was on the screen. While this did significantly improve performance, it still wasn’t up to the level I wanted.&lt;/p&gt;
&lt;p&gt;As an aside: You might notice, in this code snippet, I used the lines &lt;code&gt;virtual = self.virtual&lt;/code&gt; and &lt;code&gt;actual = self.actual&lt;/code&gt;. That was yet another attempt at optimization. The rationale behind this was from &lt;a href=&quot;https://urish.medium.com/embedded-python-cranking-performance-knob-up-to-eleven-df31a5940a63#a719&quot;&gt;this article by Uri Shaked&lt;/a&gt;, which explains that caching references from &lt;code&gt;self&lt;/code&gt; can improve performance.&lt;/p&gt;
&lt;h2 id=&quot;calling-it-quits&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#calling-it-quits&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Calling it quits&lt;/h2&gt;
&lt;p&gt;At this point, the “button-press-to-MIDI-note” latency was inconsistent and higher than I would consider acceptable. I had an application that had exploded in complexity, no good way to profile it, and no clear path to better optimization other than approaches that bordered on &lt;a href=&quot;https://en.wikipedia.org/wiki/Cargo_cult_programming&quot;&gt;cargo cult programming&lt;/a&gt;. Without a clear path forward, I decided to give up on CircuitPython.&lt;/p&gt;
&lt;p&gt;So… what to do now? The RP2040 didn’t have well-documented Arduino support at the time (&lt;a href=&quot;https://learn.adafruit.com/rp2040-arduino-with-the-earlephilhower-core&quot;&gt;this guide&lt;/a&gt; wasn’t published until this June). I decided to try out the &lt;a href=&quot;https://github.com/raspberrypi/pico-sdk&quot;&gt;RP2040 SDK&lt;/a&gt; from the Pi Foundation. I’m not super familiar with C++ and its toolchains, but how hard could it be?&lt;/p&gt;
&lt;h1 id=&quot;the-rp2040-sdk&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-rp2040-sdk&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The RP2040 SDK&lt;/h1&gt;
&lt;p&gt;The RP2040 SDK comes with great libraries… for the RP2040’s own peripherals, that is. At the time, the chip was so new that I couldn’t find compatible libraries for the NeoTrellis or the SH1107. So, I decided to try to roll my own.&lt;/p&gt;
&lt;h2 id=&quot;adafruit-seesaw&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#adafruit-seesaw&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Adafruit Seesaw&lt;/h2&gt;
&lt;p&gt;The NeoTrellis is based on a protocol that Adafruit created for I/O expansion devices called Seesaw. The protocol is built on I²C, and it is based off of multiple &lt;em&gt;modules&lt;/em&gt;, each with its own &lt;em&gt;functions&lt;/em&gt;. For instance, the NeoPixel module (which controls the RGB lighting in each key) is module number &lt;code&gt;0x0E&lt;/code&gt;. Setting a given pixel value is function number &lt;code&gt;0x04&lt;/code&gt;. There are some precise timing requirements with the protocol, but in general, it’s just about doing an I²C write to the Seesaw’s address, and setting the payload to the module and function numbers followed by any additional arguments. To read data from the Seesaw, it just takes an immediate I²C read afterward.&lt;/p&gt;
&lt;p&gt;I wrote a Seesaw driver in C++, which meant writing to NeoPixels was as easy as this:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot; data-language=&quot;cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NeoPixel&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;uint16_t&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt; g&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    number &lt;span class=&quot;token operator&quot;&gt;*=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// each pixel is 3 bytes in the buffer&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;number &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; g&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    seesaw&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        MODULE_BASE&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        BUF&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NeoPixel&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    seesaw&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        MODULE_BASE&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        SHOW&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;nullptr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reading data from the keypad was similarly just a set of Seesaw commands.&lt;/p&gt;
&lt;p&gt;With this out of the way, I was feeling pretty optimistic. Sure, I had to write my own library implementing the protocol, but at least I got something working! This is when things started to take a turn…&lt;/p&gt;
&lt;h2 id=&quot;sh1107&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#sh1107&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;SH1107&lt;/h2&gt;
&lt;p&gt;Adafruit links a &lt;a href=&quot;https://cdn-shop.adafruit.com/product-files/4650/4650_C14586.pdf&quot;&gt;datasheet&lt;/a&gt; with information about the display. However, it includes almost no information other than the I²C messages to send for the OLED startup procedure. It doesn’t even list the available I²C commands. And, parts of it are written in Chinese with either no English translation or an incomprehensible attempt at one. I ended up basing my implementation mostly off of another datasheet I found &lt;a href=&quot;https://www.displayfuture.com/Display/datasheet/controller/SH1107.pdf&quot;&gt;here&lt;/a&gt;, as well as using Adafruit’s CircuitPython SH1107 library as a reference.&lt;/p&gt;
&lt;p&gt;The SH1107 framebuffer mapping is somewhat unconventional compared to other hobbyist displays. Most displays allocate one or more bytes per pixel, but the SH1107 only uses one &lt;em&gt;bit&lt;/em&gt; per pixel, since it is a monochrome screen.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2550&quot; height=&quot;3300&quot; src=&quot;https://breq.dev/_astro/sh1107-mapping.Nm8279MD_yGBxe.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;lvgl&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#lvgl&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;LVGL&lt;/h2&gt;
&lt;p&gt;Now that I understood the SH1107 mapping, I needed a library that could draw simple shapes and text onto the screen. A popular option I found was LVGL. I had been wanting to try LVGL for a long time, mostly because it’s the default graphics library for &lt;a href=&quot;https://pros.cs.purdue.edu/&quot;&gt;PROS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I got to work porting LVGL to the SH1107, and I immediately noticed that it was going to take some trickery to get LVGL to play nice with this weird pixel mapping. Thankfully, LVGL provides a few callbacks that can be customized:&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;LVGL Display Driver Callback&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;flush_cb&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Callback to flush the display buffer data to the display hardware&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;rounder_cb&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Callback to broaden the update area if necessary to ensure it lines up with display pages&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;set_px_cb&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Callback to set a specific pixel value within the display buffer&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;I got to work on my implementations.&lt;/p&gt;
&lt;h3 id=&quot;rounder_cb&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#rounder_cb&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;rounder_cb&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This one is the most straightforward. All I needed to do was ensure that the X coordinates of the update area lined up with the page boundaries (every 8 pixels).&lt;/p&gt;
&lt;p&gt;Within a page, all the pixels in any row are stored in the same byte. Therefore, if we want to update part of the display, we need to ensure that the area we’re updating is aligned to the page boundaries, since we can’t update individual bits.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;264&quot; height=&quot;223&quot; src=&quot;https://breq.dev/_astro/sh1107-round.nkjm8Idr_ZBc9zY.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;In this diagram, each vertical black line represents a boundary between two pages. The original update area, shown in red, is extended to line up with the page boundaries.&lt;/p&gt;
&lt;p&gt;I started by rounding the first coordinate down to the nearest multiple of 8 by masking off the last three bits. Then, I did the same for the second coordinate, adding 7 to bring it to the end of the page.&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot; data-language=&quot;cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Display&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lv_disp_drv_t&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; disp_drv&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lv_area_t&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; area&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    area&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;x1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; area&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;x1 &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    area&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;x2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;area&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;x2 &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;set_px_cb&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#set_px_cb&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;set_px_cb&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Next, I took on the callback for setting pixel values.&lt;/p&gt;
&lt;p&gt;I started by calculating the page, column, and bit of the given pixel. Then came the tricky part—I found the number of bytes per page. LVGL sometimes only updates part of the display at a time, so it might decide to only update half of the display.&lt;/p&gt;
&lt;p&gt;Finally, I found the offset into the buffer, and used a mask to flip the bit.&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot; data-language=&quot;cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Display&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set_pixel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lv_disp_drv_t&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; disp_drv&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; buf&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lv_coord_t buf_w&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lv_coord_t x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lv_coord_t y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lv_color_t color&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lv_opa_t opa&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;uint16_t&lt;/span&gt; page &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;uint16_t&lt;/span&gt; column &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt; bit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt; mask &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x3C;&amp;#x3C;&lt;/span&gt; bit&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;uint16_t&lt;/span&gt; bytes_per_page &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; disp_buf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;area&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y2 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; disp_buf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;area&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y1 &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;uint16_t&lt;/span&gt; buffer_index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;page &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; bytes_per_page&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; column&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;full &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        buf&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;buffer_index&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|=&lt;/span&gt; mask&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        buf&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;buffer_index&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt;mask&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;flush_cb&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#flush_cb&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;&lt;code&gt;flush_cb&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;One more. For this one, I found the starting page, ending page, starting column, and number of bytes per page. I then iterated through each page.&lt;/p&gt;
&lt;p&gt;For each page, I set the display page address to the current page, then set the column address to the starting column (since there are more than 16 columns, this is split into 2 commands, one for the high bits and one for the low bits). Finally, I found the index into the buffer, and sent the number of bytes per page.&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot; data-language=&quot;cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Display&lt;/span&gt;&lt;span class=&quot;token double-colon punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;flush&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lv_disp_drv_t&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; disp_drv&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lv_area_t&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; area&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lv_color_t&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; color_p&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt; start_page &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; area&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;x1 &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt; end_page &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; area&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;x2 &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt; start_col &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; area&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;y1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt; end_col &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; area&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;y2 &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt; start_col_high &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;start_col &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt; start_col_low &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; start_col &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt; bytes_per_page &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; end_col &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; start_col&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; color_buffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token generic-function&quot;&gt;&lt;span class=&quot;token function&quot;&gt;reinterpret_cast&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;color_p&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt; page_offset &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; start_page &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; page_offset &lt;span class=&quot;token operator&quot;&gt;&amp;#x3C;=&lt;/span&gt; end_page&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;page_offset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;send_command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;PAGE_ADDR &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; start_page &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; page_offset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token function&quot;&gt;send_command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;COL_ADDR_LOW &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; start_col_low&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;send_command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;COL_ADDR_HIGH &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; start_col_high&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;uint16_t&lt;/span&gt; buffer_index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; page_offset &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; bytes_per_page&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;uint8_t&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; color_buffer &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; buffer_index&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token function&quot;&gt;send_data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bytes_per_page&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;lv_disp_flush_ready&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;disp_drv&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I write these as if this was a straightforward process. In reality, getting these callbacks right took me about a week of trial and error. I spent so long troubleshooting edge cases that only occurred for specific update area sizes, and struggling to understand how the addressing of the SH1107 worked to begin with. But finally, I had a working display driver. Now, all I needed was the USB functionality to send keystrokes or MIDI input to the computer.&lt;/p&gt;
&lt;h2 id=&quot;tinyusb&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#tinyusb&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;TinyUSB&lt;/h2&gt;
&lt;p&gt;The RP2040 SDK includes TinyUSB as a high-level USB library. The Pi Foundation provides no documentation for this library. The TinyUSB docs say that…&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It is relatively simple to incorporate tinyusb into your (existing) project&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;…but they provide almost no documentation. Seriously, what is “Implement all enabled classes’s [sic] callbacks”??? What classes are enabled? What classes should I enable? What callbacks do they have? What does my implementation need to include???&lt;/p&gt;
&lt;p&gt;It is at this point where I gave up on this project.&lt;/p&gt;
&lt;h1 id=&quot;conclusion&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Well, this is it. I’m faced with a project I spent countless hours on, without anything to show for it. So what did I learn?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Introducing abstractions for short-term speedup can lead to technical debt in the long run.&lt;/strong&gt; By trying to optimize the CircuitPython build as much as possible, I introduced complexity that left me with an application that was harder to understand. Without clear knowledge as to what my code was doing, I was left without any clear way to improve it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The flashiest solution isn’t always the best.&lt;/strong&gt; If I had picked a chip with stable Arduino support, I could’ve taken advantage of existing libraries while keeping the speed of C++. Choosing the brand new RP2040 put me on the bleeding edge.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pick the right tool for the job.&lt;/strong&gt; Python, on a microcontroller, for a latency-critical application… Even though CircuitPython let me get up and running quickly, it couldn’t achieve what I was targeting, forcing me to rewrite everything from scratch in C++.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fail early.&lt;/strong&gt; One of the main dealbreakers of the project was that the keypad buttons didn’t respond well to “drumming” input—they needed to be completely pressed down. Instead of recognizing this flaw in the design, I kept investing effort into the project anyway.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Focus on the MVP.&lt;/strong&gt; In the early stages, I invested a lot of time in building out the app-specific hot reload feature. If I had only focused on the core MIDI functionality, I would have faced the latency and usability issues a lot sooner. By worrying about side features instead of the minimum viable product, I was delaying the inevitable.&lt;/p&gt;
&lt;h2 id=&quot;epilogue&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#epilogue&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Epilogue&lt;/h2&gt;
&lt;p&gt;Ironically, two weeks after I gave up on this project, Adafruit released an extremely similar design &lt;a href=&quot;https://www.adafruit.com/product/5128&quot;&gt;as a kit&lt;/a&gt;, trading the three buttons for an encoder and the silicone keypad for Cherry MX (clone) switches.&lt;/p&gt;
&lt;p&gt;I’m not disappointed in myself for how this project went. I learned more about bit operations in embedded programming, the mechanics of I²C, and the tooling required to manage a large C++ project. I also learned some important lessons about project management and planning. I’m glad I’m experiencing failure like this now, when the only casualty is a bit of my spare time.&lt;/p&gt;
&lt;p&gt;Oh, and when I said I had nothing to show… that isn’t &lt;em&gt;entirely&lt;/em&gt; true.&lt;/p&gt;
&lt;youtube id=&quot;4HvcbrAWKGY&quot;&gt;&lt;/youtube&gt;</content:encoded></item><item><title>Motion Sickness Fish</title><link>https://breq.dev/projects/fish/</link><guid isPermaLink="true">https://breq.dev/projects/fish/</guid><description>Billy Bass fish modified to sing a Phoebe Bridgers song</description><pubDate>Tue, 08 Jun 2021 00:00:00 GMT</pubDate><content:encoded>&lt;iframe src=&quot;https://www.youtube.com/embed/iX1t1ADwkrk?si=s_ofJ9itNmFts5d6&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; class=&quot;w-full aspect-video rounded-2xl overflow-clip&quot;&gt;&lt;/iframe&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;Exactly what it says on the tin. It’s a Billy Bass fish that’s had its electronics replaced and had Motion Sickness loaded on.&lt;/p&gt;
&lt;p&gt;For those of you lucky enough to not know what the Billy Bass fish is, let me enlighten you:&lt;/p&gt;
&lt;iframe src=&quot;https://www.youtube.com/embed/AJF2cCMXPKk?si=s_ofJ9itNmFts5d6&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; class=&quot;w-full aspect-video rounded-2xl overflow-clip&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;And here’s the song I loaded on:&lt;/p&gt;
&lt;div style=&quot;display: flex; justify-content: center;&quot;&gt;
    &lt;iframe src=&quot;https://open.spotify.com/embed/track/6LxcPUqx6noURdA5qc4BAT&quot; width=&quot;300&quot; height=&quot;380&quot; frameborder=&quot;0&quot; allowtransparency=&quot;true&quot; allow=&quot;encrypted-media&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;My uncle and aunt asked me to take this one on. The entire project needed to be finished in about a week. I had finals for the first half of the week, and I knew it would take a while to ship to my relatives in California, but I concluded that I could barely make it work if I ordered the parts right away, rebuilt the fish as quickly as possible once they arrived, and shipped the result as soon as possible.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;h2 id=&quot;hardware-and-construction&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#hardware-and-construction&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Hardware and Construction&lt;/h2&gt;
&lt;p&gt;The chip that controls Billy Bass and plays the default music is covered in epoxy. Much smarter people than me &lt;a href=&quot;https://www.youtube.com/watch?v=_E0PWQvW-14&quot;&gt;struggle&lt;/a&gt; to do this kind of thing. I quickly ruled this option out.&lt;/p&gt;
&lt;p&gt;What about using the existing speakers though? Well, at the time, I didn’t have a physical Billy Bass handy (remember, shipping…) so I couldn’t be sure of the specifications of these speakers.&lt;/p&gt;
&lt;p&gt;The easiest solution would be to completely replace the built-in electronics with my own.Knowing that I had only a few days to get this working, and no opportunity to re-order parts if something went wrong, I knew that I needed to get this right.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Normally, I would DIY as much as possible, with hand-soldered perfboards, hectic wiring, and all sorts of bodged-together boards. It became obvious to me that this was the wrong approach here. This was my first lesson: know when too much DIY just won’t cut it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I needed a cohesive ecosystem. &lt;a href=&quot;https://adafruit.com/&quot;&gt;Adafruit&lt;/a&gt; has been my go-to source for parts over the past couple years, and being based in NYC, I knew shipping wouldn’t take too long. But that still left me with plenty of options.&lt;/p&gt;
&lt;p&gt;I started by identifying what physical hardware I’d need. Adafruit has a line of &lt;a href=&quot;https://www.adafruit.com/?q=VS1053&amp;#x26;sort=BestMatch&quot;&gt;boards&lt;/a&gt; based off of the VS1053 chip—that takes care of my audio playing. And there are plenty of motor boards available in any form factor I would want. So what do I choose?&lt;/p&gt;
&lt;p&gt;Adafruit’s board selection generally varies by size and power supply. The VS1053 modules were available in 3 different form factors: a simple breakout board, a FeatherWing board, and an Arduino Shield board.&lt;/p&gt;
&lt;p&gt;The breakout board would require wiring it up to a board myself, which eliminated it from the running. Feather boards and Arduino boards are available with any sort of chip, so that’s not a concern. What differentiates the boards is physical size and power supply. Feathers are small, and can be powered off of a 5 volt source or a LiPo battery. Arduinos are much larger, but they can take anything from 6 to 20 volts.&lt;/p&gt;
&lt;p&gt;Here I faced a dilemma. I wanted the electronics to be small, so that they could fit inside the Billy Bass enclosure without any sort of external box. I also was wary of running the chip and the motors off the same power rail, due to reset issues I’d experienced in the past. The Arduino, with it’s built-in power regulator, seemed like functionally the better choice, as it could take a standard barrel plug power supply, run the motors directly off of it, and run the microcontroller chip off a clean, regulated 5V.&lt;/p&gt;
&lt;p&gt;However, I wasn’t sure if the larger Arduino form factor would fit. I considered rigging something up with a custom perfboard to attach a voltage regulator to the Feather… but I eventually decided against it. I needed to play by the rules of the ecosystem so that I could take advantage of its ease-of-use. I bought the VS1053 and the motor driver for the Arduino.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1720&quot; height=&quot;1290&quot; src=&quot;https://breq.dev/_astro/fish.rzcTkMNi_cWyq1.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;
Here&apos;s the finished product! You can see the Adafruit Metro (Arduino clone),
the speakers, and not much else. Because the VS1053 chip and motor driver were
mounted directly on top of the Metro, there was no need for messy mounting
solutions or manual wiring.
&lt;/p&gt;
&lt;h2 id=&quot;movement-sync-software&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#movement-sync-software&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Movement Sync Software&lt;/h2&gt;
&lt;p&gt;And now, the race was on to get something functional as fast as possible. Getting the song to play over the audio shield and speakers was easy, thanks to the well-documented Adafruit libraries. That left only the fish motion to take care of.&lt;/p&gt;
&lt;p&gt;Getting something working this quickly would have been impossible if I had tried to roll my own amplifier circuit, or wire together my own power supply circuit, or do any of that. Here was the ecosystem at work for me, allowing me to go from idea to concept in record time.&lt;/p&gt;
&lt;p&gt;The next step was to actually move the fish. I quickly wrote functions to control the fish’s head, tail, and mouth, again using the excellent library Adafruit provided for their motor shield. Normally, I would’ve hacked something together with an L293D in order to save a buck or two. But in this case, the motor shield was worth every penny.&lt;/p&gt;
&lt;p&gt;Now, the question is, how do I decide when to activate these movements?&lt;/p&gt;
&lt;h3 id=&quot;onboard-audio-processing&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#onboard-audio-processing&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Onboard Audio Processing?&lt;/h3&gt;
&lt;p&gt;The official Amazon Alexa enabled Billy Bass reacts to the audio to decide when to move. This… doesn’t exactly work well, as you can see in this Linus Tech Tips review at around the 5:03 mark:&lt;/p&gt;
&lt;youtube id=&quot;4EzC9J9xOHQ&quot;&gt;
&lt;p&gt;“Okay, so he just kind of spasms then. So basically, you just have like a fish out of water on your wall whenever music is playing.”&lt;/p&gt;
&lt;p&gt;Yeah… that wasn’t gonna cut it.&lt;/p&gt;
&lt;p&gt;I decided I needed to manually choreograph the movements to match the music. No reacting-to-audio trickery would save me from this. I did attempt a few basic tests of reacting to audio loudness, but my results were even worse than the fish in that video.&lt;/p&gt;
&lt;h3 id=&quot;pre-recording-movements&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#pre-recording-movements&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Pre-Recording Movements&lt;/h3&gt;
&lt;p&gt;I realized that I would need to write some custom software if I wanted to precisely record all of these movements in relation to the song. I needed a framework that would let me play audio, handle raw keyboard events, and write in a language I could iterate quickly in. PyGame fit the bill, and I was quickly off to the races.&lt;/p&gt;
&lt;p&gt;My script waited for the user to press the space key. It then started playing the music and keeping an internal timer. Then, whenever another key was pressed, it would log the event and the time at which it happened. Finally, it saved it to a JSON file. Pretty basic stuff.&lt;/p&gt;
&lt;p&gt;Did I have to decouple the recording from the code generation? Probably not. But I figured it would be best to have a full record of all the movements over the course of the song, so I could try different methods of storing that on the fish if I needed to.&lt;/p&gt;
&lt;h3 id=&quot;storing-the-routine&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#storing-the-routine&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Storing the routine&lt;/h3&gt;
&lt;p&gt;If I had the luxury of time, I might’ve gone with a packed binary representation like I did for my &lt;a href=&quot;https://breq.dev/projects/stmusic&quot;&gt;STMusic&lt;/a&gt; project. However, writing the code to generate, store, and load this packed representation would be a lot of work.&lt;/p&gt;
&lt;p&gt;I eventually landed on the idea of using a Python script to take the JSON object and &lt;em&gt;generate C code&lt;/em&gt; that called each movement function.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; json

&lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;recorded.json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    actions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;load&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

code &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

action_lines &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;head&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;  fish.head();&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;tail&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;  fish.tail();&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;rest&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;  fish.rest();&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;mouthOpen&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;  fish.setMouth(1);&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;mouthClosed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;  fish.setMouth(0);&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

total_ts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; timestamp&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; actions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    total_ts &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timestamp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    millis &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;total_ts&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    code&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;append&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;  delayUntil(&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;millis&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;);&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    code&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;append&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action_lines&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

generated &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;join&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;code&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;routine.ino&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;w&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;write&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;void routine() {\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;write&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;generated&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;write&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\n}\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Give me a second, I have to wash the blood off my hands after murdering every best practice in the book with this move.&lt;/p&gt;
&lt;p&gt;To be clear—this method is &lt;em&gt;ugly&lt;/em&gt;. It produces an excessively large binary, it stores data in a file that people expect to be for application logic, it adds an unintuitive step to the build process, it’s just generally Not The Way Things Are Supposed To Be Done.&lt;/p&gt;
&lt;p&gt;But it worked. It kept the movements synched up to the song. At the end of the day, the future maintainability or extensibility of this program isn’t something that mattered that much, and it was a worthwhile tradeoff in order to save some valuable time.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This was the second major lesson. Code quality is good, and good habits exist for a reason, but it’s important to decide when things are “good enough” for that specific project.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;one-last-feature-the-volume-knob&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#one-last-feature-the-volume-knob&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;One Last Feature: the volume knob&lt;/h3&gt;
&lt;p&gt;After putting everything together, I noticed that the fish was perhaps too loud to have in a room. At the last minute, I decided to try adding a volume knob. But how to do it?&lt;/p&gt;
&lt;p&gt;The “knob” part was easy enough: I just grabbed some spare parts from my dad’s work on repairing guitar amps. On the other hand, the “volume” part presented more of a challenge. Should I go for an analog approach, and try to create a circuit involving the knob that limited the output to the speaker? Or…&lt;/p&gt;
&lt;p&gt;I had another idea. What if I used an analog input pin on the Arduino to read the value of the knob, then sent that value to the VS1053 to adjust it’s output volume in software? This greatly simplified the wiring, so I went ahead and built it.&lt;/p&gt;
&lt;p&gt;Here, I made another tradeoff. I sampled this knob after each movement—not at a regular interval, not at a reasonable sample rate, literally just after each fish movement. And it was good enough! The variable amplitude of the song made the jagged nature of this sampling less obvious.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;Compromises were definitely made. The lack of built-in batteries made the project significantly more cumbersome, for one. And the motions weren’t completely perfect. That said, I’m pretty proud of making what I did in such a limited turnaround time.&lt;/p&gt;
&lt;p&gt;This project taught me when good enough was good enough. I sometimes struggle with perfectionism in my projects, where I’ll rewrite the same code over and over again in pursuit of a better way of doing things. This forced me to make the tradeoffs I was too scared to make, and to decide what mattered for the final product and what didn’t.&lt;/p&gt;&lt;/youtube&gt;</content:encoded></item><item><title>5F3759DF: An explanation of the world&apos;s most infamous magic number</title><link>https://breq.dev/2021/03/17/5f3759df/</link><guid isPermaLink="true">https://breq.dev/2021/03/17/5f3759df/</guid><description>Here&apos;s a famous function in the Quake III source code. Can you guess what it does?</description><pubDate>Wed, 17 Mar 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Here’s a famous function in the Quake III source code. Can you guess what it does?&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot; data-language=&quot;cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Q_rsqrt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; number &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; x2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; threehalfs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.5F&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	x2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5F&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	y  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	i  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;                       &lt;span class=&quot;token comment&quot;&gt;// evil floating point bit level hacking&lt;/span&gt;
	i  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x5f3759df&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;               &lt;span class=&quot;token comment&quot;&gt;// what the fuck?&lt;/span&gt;
	y  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	y  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; threehalfs &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; x2 &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; y &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// 1st iteration&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//	y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function calculates the inverse of the square root of a number.&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;Q&lt;/mi&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;_&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;mi&gt;q&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;u&lt;/mi&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mrow&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;u&lt;/mi&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;/mrow&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;Q\_rsqrt(number) = \frac{1}{\sqrt{number}}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.06em;vertical-align:-0.31em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;mord&quot; style=&quot;margin-right:0.02778em;&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;rs&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.02778em;&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;mb&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.02778em;&quot;&gt;er&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.2514em;vertical-align:-0.93em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.1778em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord sqrt&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.9322em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot; style=&quot;padding-left:0.833em;&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;mb&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.02778em;&quot;&gt;er&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.8922em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail&quot; style=&quot;min-width:0.853em;height:1.08em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.08em&quot; viewBox=&quot;0 0 400000 1080&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.1078em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.93em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;h1 id=&quot;preliminary-explanation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#preliminary-explanation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Preliminary Explanation&lt;/h1&gt;
&lt;h2 id=&quot;why&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#why&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Why?&lt;/h2&gt;
&lt;p&gt;Quake III needs to perform a lot of calculations to simulate lighting. Each face of a polygon in the scene is represented by a 3D vector pointing perpendicular to it. Then, these vectors are used to calculate the reflections of light off of the face.&lt;/p&gt;
&lt;p&gt;Each face could be represented by a vector of any length—the important information is the direction of the vector, not the length of the vector. However, the calculations become much more simple if all of the vectors have length 1. Thus, we need to &lt;em&gt;normalize&lt;/em&gt; these vectors. We can do this by dividing the vector by its length (it’s &lt;em&gt;norm&lt;/em&gt;, &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;∥&lt;/mi&gt;&lt;mi mathvariant=&quot;bold-italic&quot;&gt;v&lt;/mi&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;∥&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\| \boldsymbol{v} \|&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;∥&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord boldsymbol&quot; style=&quot;margin-right:0.03704em;&quot;&gt;v&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;∥&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; ). Let &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi mathvariant=&quot;bold-italic&quot;&gt;v&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\boldsymbol{v}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord boldsymbol&quot; style=&quot;margin-right:0.03704em;&quot;&gt;v&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; denote the vector before normalization and &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;&lt;mover accent=&quot;true&quot;&gt;&lt;mi mathvariant=&quot;bold-italic&quot;&gt;v&lt;/mi&gt;&lt;mo&gt;^&lt;/mo&gt;&lt;/mover&gt;&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\boldsymbol{\hat{v}}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7079em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord accent&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7079em;&quot;&gt;&lt;span style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord boldsymbol&quot; style=&quot;margin-right:0.03704em;&quot;&gt;v&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.0134em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;accent-body&quot; style=&quot;left:-0.2875em;&quot;&gt;&lt;span class=&quot;mord mathbf&quot;&gt;^&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; denote the normalized vector.&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;&lt;mover accent=&quot;true&quot;&gt;&lt;mi mathvariant=&quot;bold-italic&quot;&gt;v&lt;/mi&gt;&lt;mo&gt;^&lt;/mo&gt;&lt;/mover&gt;&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mi mathvariant=&quot;bold-italic&quot;&gt;v&lt;/mi&gt;&lt;mrow&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;∥&lt;/mi&gt;&lt;mi mathvariant=&quot;bold-italic&quot;&gt;v&lt;/mi&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;∥&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\boldsymbol{\hat{v}} = \frac{\boldsymbol{v}}{ \| \boldsymbol{v} \| }&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7079em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord accent&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7079em;&quot;&gt;&lt;span style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord boldsymbol&quot; style=&quot;margin-right:0.03704em;&quot;&gt;v&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.0134em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;accent-body&quot; style=&quot;left:-0.2875em;&quot;&gt;&lt;span class=&quot;mord mathbf&quot;&gt;^&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0574em;vertical-align:-0.936em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.1214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;∥&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord boldsymbol&quot; style=&quot;margin-right:0.03704em;&quot;&gt;v&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;∥&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord boldsymbol&quot; style=&quot;margin-right:0.03704em;&quot;&gt;v&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.936em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Using the Pythagorean theorem:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;∥&lt;/mi&gt;&lt;mi mathvariant=&quot;bold-italic&quot;&gt;v&lt;/mi&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;∥&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msqrt&gt;&lt;mrow&gt;&lt;msubsup&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msubsup&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msubsup&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;z&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;/msqrt&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\| \boldsymbol{v} \| = \sqrt{v_x^2 + v_y^2 + v_z^2}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;∥&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord boldsymbol&quot; style=&quot;margin-right:0.03704em;&quot;&gt;v&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;∥&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.84em;vertical-align:-0.6276em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord sqrt&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.2124em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3.8em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.8em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot; style=&quot;padding-left:1em;&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.453em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.247em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.453em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3831em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.453em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.04398em;&quot;&gt;z&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.247em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.1724em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.8em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail&quot; style=&quot;min-width:1.02em;height:1.88em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.88em&quot; viewBox=&quot;0 0 400000 1944&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M983 90
l0 -0
c4,-6.7,10,-10,18,-10 H400000v40
H1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7
s-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744
c-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30
c26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722
c56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5
c53.7,-170.3,84.5,-266.8,92.5,-289.5z
M1001 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6276em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;&lt;mover accent=&quot;true&quot;&gt;&lt;mi mathvariant=&quot;bold-italic&quot;&gt;v&lt;/mi&gt;&lt;mo&gt;^&lt;/mo&gt;&lt;/mover&gt;&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi mathvariant=&quot;bold-italic&quot;&gt;v&lt;/mi&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mrow&gt;&lt;msubsup&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msubsup&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msubsup&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;z&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\boldsymbol{\hat{v}} = \boldsymbol{v} \cdot \frac{1}{\sqrt{v_x^2 + v_y^2 + v_z^2}}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7079em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord accent&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7079em;&quot;&gt;&lt;span style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord boldsymbol&quot; style=&quot;margin-right:0.03704em;&quot;&gt;v&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.0134em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;accent-body&quot; style=&quot;left:-0.2875em;&quot;&gt;&lt;span class=&quot;mord mathbf&quot;&gt;^&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4445em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord boldsymbol&quot; style=&quot;margin-right:0.03704em;&quot;&gt;v&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:3.0514em;vertical-align:-1.73em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.11em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.1635em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord sqrt&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.1635em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3.8em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.8em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot; style=&quot;padding-left:1em;&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.453em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.247em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.453em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3831em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.453em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.04398em;&quot;&gt;z&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.247em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.1235em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.8em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail&quot; style=&quot;min-width:1.02em;height:1.88em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.88em&quot; viewBox=&quot;0 0 400000 1944&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M983 90
l0 -0
c4,-6.7,10,-10,18,-10 H400000v40
H1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7
s-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744
c-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30
c26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722
c56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5
c53.7,-170.3,84.5,-266.8,92.5,-289.5z
M1001 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6765em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.3935em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.1635em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.8405em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.1635em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.73em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;We can then break this down into each component (x, y, and z) of the 3-dimensional vector:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mover accent=&quot;true&quot;&gt;&lt;msub&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;^&lt;/mo&gt;&lt;/mover&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mrow&gt;&lt;msubsup&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msubsup&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msubsup&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;z&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\hat{v_x} = v_x \cdot \frac{1}{\sqrt{v_x^2 + v_y^2 + v_z^2}}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8444em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord accent&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6944em;&quot;&gt;&lt;span style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.1514em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;accent-body&quot; style=&quot;left:-0.25em;&quot;&gt;&lt;span class=&quot;mord&quot;&gt;^&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.5945em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.1514em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:3.0514em;vertical-align:-1.73em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.11em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.1635em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord sqrt&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.1635em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3.8em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.8em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot; style=&quot;padding-left:1em;&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.453em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.247em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.453em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3831em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.453em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.04398em;&quot;&gt;z&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.247em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.1235em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.8em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail&quot; style=&quot;min-width:1.02em;height:1.88em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.88em&quot; viewBox=&quot;0 0 400000 1944&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M983 90
l0 -0
c4,-6.7,10,-10,18,-10 H400000v40
H1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7
s-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744
c-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30
c26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722
c56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5
c53.7,-170.3,84.5,-266.8,92.5,-289.5z
M1001 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6765em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.3935em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.1635em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.8405em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.1635em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.73em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mover accent=&quot;true&quot;&gt;&lt;msub&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;^&lt;/mo&gt;&lt;/mover&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mrow&gt;&lt;msubsup&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msubsup&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msubsup&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;z&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\hat{v_y} = v_y \cdot \frac{1}{\sqrt{v_x^2 + v_y^2 + v_z^2}}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9805em;vertical-align:-0.2861em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord accent&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6944em;&quot;&gt;&lt;span style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.1514em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;accent-body&quot; style=&quot;left:-0.25em;&quot;&gt;&lt;span class=&quot;mord&quot;&gt;^&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7306em;vertical-align:-0.2861em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.1514em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2861em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:3.0514em;vertical-align:-1.73em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.11em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.1635em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord sqrt&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.1635em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3.8em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.8em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot; style=&quot;padding-left:1em;&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.453em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.247em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.453em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3831em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.453em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.04398em;&quot;&gt;z&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.247em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.1235em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.8em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail&quot; style=&quot;min-width:1.02em;height:1.88em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.88em&quot; viewBox=&quot;0 0 400000 1944&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M983 90
l0 -0
c4,-6.7,10,-10,18,-10 H400000v40
H1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7
s-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744
c-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30
c26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722
c56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5
c53.7,-170.3,84.5,-266.8,92.5,-289.5z
M1001 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6765em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.3935em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.1635em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.8405em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.1635em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.73em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mover accent=&quot;true&quot;&gt;&lt;msub&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;z&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;^&lt;/mo&gt;&lt;/mover&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;z&lt;/mi&gt;&lt;/msub&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mrow&gt;&lt;msubsup&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msubsup&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msubsup&gt;&lt;mi&gt;v&lt;/mi&gt;&lt;mi&gt;z&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\hat{v_z} = v_z \cdot \frac{1}{\sqrt{v_x^2 + v_y^2 + v_z^2}}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8444em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord accent&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6944em;&quot;&gt;&lt;span style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.1514em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.04398em;&quot;&gt;z&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;accent-body&quot; style=&quot;left:-0.25em;&quot;&gt;&lt;span class=&quot;mord&quot;&gt;^&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.5945em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.1514em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.04398em;&quot;&gt;z&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:3.0514em;vertical-align:-1.73em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.11em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.1635em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord sqrt&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.1635em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3.8em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.8em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot; style=&quot;padding-left:1em;&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.453em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.247em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.453em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3831em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.453em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.04398em;&quot;&gt;z&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.247em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.1235em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.8em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail&quot; style=&quot;min-width:1.02em;height:1.88em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.88em&quot; viewBox=&quot;0 0 400000 1944&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M983 90
l0 -0
c4,-6.7,10,-10,18,-10 H400000v40
H1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7
s-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744
c-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30
c26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722
c56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5
c53.7,-170.3,84.5,-266.8,92.5,-289.5z
M1001 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6765em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.3935em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.1635em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.8405em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3.1635em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.73em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;The addition and multiplication are easy enough to execute, but both the division and the square root would be very slow on a Quake III-era CPU. Thus, we need a faster method to return an approximate value of &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{1}{\sqrt{x}}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.3831em;vertical-align:-0.538em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em;&quot;&gt;&lt;span style=&quot;top:-2.6259em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord sqrt mtight&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8059em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mtight&quot; style=&quot;padding-left:0.833em;&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.7659em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail mtight&quot; style=&quot;min-width:0.853em;height:1.08em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.08em&quot; viewBox=&quot;0 0 400000 1080&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2341em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.538em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h2 id=&quot;ieee-754-how-computers-store-fractions&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#ieee-754-how-computers-store-fractions&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;IEEE 754: How Computers Store Fractions&lt;/h2&gt;
&lt;p&gt;It’s pretty straightforward to store an integer in binary by just converting it into base 2:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0000010&lt;/mn&gt;&lt;msub&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;5_{10} = 0000 0101_2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7944em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7944em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0000010&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;But how do computers store fractions? One simple approach would be to put a decimal point at a fixed position in the number:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0101.000&lt;/mn&gt;&lt;msub&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;5_{10} = 0101 . 0000_2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7944em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7944em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0101.000&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;5.2&lt;/mn&gt;&lt;msub&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0101.010&lt;/mn&gt;&lt;msub&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;5.25_{10} = 0101 . 0100_2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7944em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;5.2&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7944em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0101.010&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;This seems to work well, but we’ve drastically reduced the size of the numbers we can store. We only have half as many bits to store our integer part, which limits us to a relatively small range of numbers, and we only have half as many bits for our fractional part, which doesn’t give us a ton of precision.&lt;/p&gt;
&lt;p&gt;Instead, we can borrow the idea of scientific notation, which represents numbers with a mantissa and exponent like &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;5.43&lt;/mn&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msup&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;5.43 \cdot 10^3&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;5.43&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. With this, we can store very large values and very small values with a consistently low relative error. This is called “floating point”—unlike the prior idea, the decimal point can effectively “float around” in the bit representation to give us precision at both very small and very large values. Thus, if &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;x&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; is the number we want to store, we write it in terms of exponent &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;E&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; and mantissa &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;M&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;M&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;x = M \cdot 2^E&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8913em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8913em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;At the time Quake III was released, most CPUs were 32-bit. Thus, the floating point representation used 32 bits to store a number. They were allocated as follows:&lt;/p&gt;















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;1 bit&lt;/th&gt;&lt;th&gt;8 bits&lt;/th&gt;&lt;th&gt;23 bits&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;sign&lt;/td&gt;&lt;td&gt;exponent&lt;/td&gt;&lt;td&gt;mantissa&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The “sign” bit is used to represent whether the number was positive or negative. In this case, since we know the argument to the square root function should always be positive, we can assume it to always be zero. There are also some special cases that happen for special values like &lt;code&gt;NaN&lt;/code&gt;, &lt;code&gt;inf&lt;/code&gt;, or very small numbers, which we can also ignore for now.&lt;/p&gt;
&lt;p&gt;Note that the exponent could be positive or negative. To accommodate this, it is stored with an offset of 127. For example, to store &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1.01&lt;/mn&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;5_{10} = 1.01 \cdot 2^2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7944em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1.01&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, we would take the exponent &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msub&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;2_{10} = 10_2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7944em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7944em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; and add &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;12&lt;/mn&gt;&lt;msub&gt;&lt;mn&gt;7&lt;/mn&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0111111&lt;/mn&gt;&lt;msub&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;127_{10} = 0111 1111_2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7944em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7944em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0111111&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; to it to get &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;1000000&lt;/mn&gt;&lt;msub&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;1000 0001_2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7944em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;In scientific notation, the first digit of the mantissa must be between 1 and 9. In this “binary scientific notation”, the first digit must be between 1 and 1. (You can see this in our example: &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1.01&lt;/mn&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;5_{10} = 1.01 \cdot 2^2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7944em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1.01&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.) Therefore, we don’t actually have to store it.&lt;/p&gt;
&lt;h2 id=&quot;interpreting-floats-as-ints&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#interpreting-floats-as-ints&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Interpreting floats as ints&lt;/h2&gt;
&lt;p&gt;An integer is just represented as a base 2 number. So, what happens if we take a number, find the bits of its floating-point representation, and then interpret those bits as a base 2 integer number?&lt;/p&gt;
&lt;p&gt;Recall that our float is stored as:&lt;/p&gt;















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;1 bit&lt;/th&gt;&lt;th&gt;8 bits&lt;/th&gt;&lt;th&gt;23 bits&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;sign&lt;/td&gt;&lt;td&gt;exponent representation&lt;/td&gt;&lt;td&gt;mantissa representation&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;(Note that the least significant bit is on the right.)&lt;/p&gt;
&lt;p&gt;Observe that the mantissa bits start in the ones place. However, remember that the mantissa is a fraction, so we’ve really stored &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(M - 1) \cdot 2^{23}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;The exponent bits, &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;E + 127&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7667em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, start in the &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;2^{23}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; place.&lt;/p&gt;
&lt;p&gt;Essentially, we have the sum of the quantity &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(M - 1) \cdot 2^{23}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; and the quantity &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(E + 127) \cdot 2^{23}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Let’s define the function &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;I(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; that represents this bizarre operation of taking a floating-point representation and interpreting it as an integer:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;I(x) = 2^{23} \cdot (E + 127 + M - 1)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7667em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;h2 id=&quot;an-observation-about-logarithms&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#an-observation-about-logarithms&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;An observation about logarithms&lt;/h2&gt;
&lt;p&gt;Let’s go back to our floating-point representation of &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;x&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;x = M \cdot 2^E&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8913em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8913em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Now, what happens if we take the logarithm? Let’s take the log base 2, since we’re working with binary. Evaluating &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\log_2(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; directly would be pretty slow, but let’s proceed symbolically to see if we get anywhere useful.&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\log_2(x) = \log_2(M \cdot 2^E)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1413em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8913em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\log_2(x) = \log_2(M) + \log_2(2^E)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1413em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8913em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\log_2(x) = \log_2(M) + E&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Calculating &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\log_2(M)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; would be hard. Instead, we can make do with an approximation. Remember that since &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;M&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;M&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; is the mantissa, it will be greater than 1 but less than 2.&lt;/p&gt;
&lt;p&gt;There’s a conveninent approximation we can use. Here’s a graph in Desmos:&lt;/p&gt;
&lt;desmos id=&quot;yvwmijecdr&quot;&gt;
&lt;p&gt;The red curve is &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\log_2(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, the function we want to approximate. The purple curve is the line &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;x - 1&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6667em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, which is already a pretty good approximation. However, the blue curve is even better: &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;x - 1 + \sigma&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6667em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, where &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\sigma&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; is tuned for the maximum accuracy around &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0.0450466&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\sigma = 0.0450466&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0.0450466&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Let’s continue, using &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\log_2(x) \approx x - 1 + \sigma&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6667em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\log_2(x) = \log_2(M) + E&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\log_2(x) \approx M - 1 + \sigma + E&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7667em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6667em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;h2 id=&quot;but-what-does-this-have-to-do-with-ix&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#but-what-does-this-have-to-do-with-ix&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;But what does this have to do with &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;I(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;?&lt;/h2&gt;
&lt;p&gt;Recall that&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;I(x) = 2^{23} \cdot (E + 127 + M - 1)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7667em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;We proceed:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;I(x) = 2^{23} \cdot (M - 1 + \sigma + E + 127 - \sigma)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6667em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7667em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Observe the &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(M - 1 + \sigma + E)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6667em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; part. Earlier, we found that &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mi&gt;M&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;E&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\log_2(x) \approx M - 1 + \sigma + E&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7667em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10903em;&quot;&gt;M&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6667em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.05764em;&quot;&gt;E&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. Therefore, we can substitute this in:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;I(x) \approx 2^{23} \cdot (\log_2(x) + 127 - \sigma)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;We can solve for &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\log_2(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; as follows:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;/mfrac&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{I(x)}{2^{23}} \approx \log_2(x) + 127 - \sigma&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.113em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.427em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;/mfrac&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{I(x)}{2^{23}} - 127 + \sigma \approx \log_2(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.113em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.427em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4831em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;/mfrac&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\log_2(x) \approx \frac{I(x)}{2^{23}} - 127 + \sigma&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.113em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.427em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;And there it is: we have a much faster way to compute the logarithm.&lt;/p&gt;
&lt;h2 id=&quot;rewrite-the-problem&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#rewrite-the-problem&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Rewrite the problem&lt;/h2&gt;
&lt;p&gt;Remember, we’re trying to find the quantity &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{1}{\sqrt{x}}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.3831em;vertical-align:-0.538em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em;&quot;&gt;&lt;span style=&quot;top:-2.6259em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord sqrt mtight&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8059em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mtight&quot; style=&quot;padding-left:0.833em;&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.7659em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail mtight&quot; style=&quot;min-width:0.853em;height:1.08em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.08em&quot; viewBox=&quot;0 0 400000 1080&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2341em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.538em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;We can use the properties of logarithms to find:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;msup&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\log_2{\frac{1}{\sqrt{x}}} = \log_2{x^{-\frac{1}{2}}} = -\frac{1}{2} \log_2{x}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.2514em;vertical-align:-0.93em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.3097em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord sqrt&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8003em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot; style=&quot;padding-left:0.833em;&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.7603em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail&quot; style=&quot;min-width:0.853em;height:1.08em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.08em&quot; viewBox=&quot;0 0 400000 1080&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2397em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.93em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.2482em;vertical-align:-0.2441em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.004em;&quot;&gt;&lt;span style=&quot;top:-3.413em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mopen nulldelimiter sizing reset-size3 size6&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8443em;&quot;&gt;&lt;span style=&quot;top:-2.656em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size3 size1 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.2255em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line mtight&quot; style=&quot;border-bottom-width:0.049em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.384em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size3 size1 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.344em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter sizing reset-size3 size6&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Now, let’s try substituting in our fast logarithm:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;msub&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\log_2{\frac{1}{\sqrt{x}}} = -\frac{1}{2} \log_2{x}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.2514em;vertical-align:-0.93em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.3097em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord sqrt&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8003em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot; style=&quot;padding-left:0.833em;&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.7603em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail&quot; style=&quot;min-width:0.853em;height:1.08em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.08em&quot; viewBox=&quot;0 0 400000 1080&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2397em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.93em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop&quot;&gt;&lt;span class=&quot;mop&quot;&gt;lo&lt;span style=&quot;margin-right:0.01389em;&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.207em;&quot;&gt;&lt;span style=&quot;top:-2.4559em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2441em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;/mfrac&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mrow&gt;&lt;mo fence=&quot;true&quot;&gt;[&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;/mfrac&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo fence=&quot;true&quot;&gt;]&lt;/mo&gt;&lt;/mrow&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{I(\frac{1}{\sqrt{x}})}{2^{23}} - 127 + \sigma \approx -\frac{1}{2} \left[ \frac{I(x)}{2^{23}} - 127 + \sigma \right]&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.4591em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.7731em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.928em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em;&quot;&gt;&lt;span style=&quot;top:-2.6259em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord sqrt mtight&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8059em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mtight&quot; style=&quot;padding-left:0.833em;&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.7659em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail mtight&quot; style=&quot;min-width:0.853em;height:1.08em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.08em&quot; viewBox=&quot;0 0 400000 1080&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2341em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.538em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4831em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.4em;vertical-align:-0.95em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;minner&quot;&gt;&lt;span class=&quot;mopen delimcenter&quot; style=&quot;top:0em;&quot;&gt;&lt;span class=&quot;delimsizing size3&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.427em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mclose delimcenter&quot; style=&quot;top:0em;&quot;&gt;&lt;span class=&quot;delimsizing size3&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;We can do some manipulation to solve for our result:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;/mfrac&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mrow&gt;&lt;mo fence=&quot;true&quot;&gt;[&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;/mfrac&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo fence=&quot;true&quot;&gt;]&lt;/mo&gt;&lt;/mrow&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{I(\frac{1}{\sqrt{x}})}{2^{23}} - 127 + \sigma \approx -\frac{1}{2} \left[ \frac{I(x)}{2^{23}} - 127 + \sigma \right]&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.4591em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.7731em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.928em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em;&quot;&gt;&lt;span style=&quot;top:-2.6259em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord sqrt mtight&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8059em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mtight&quot; style=&quot;padding-left:0.833em;&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.7659em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail mtight&quot; style=&quot;min-width:0.853em;height:1.08em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.08em&quot; viewBox=&quot;0 0 400000 1080&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2341em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.538em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4831em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.4em;vertical-align:-0.95em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;minner&quot;&gt;&lt;span class=&quot;mopen delimcenter&quot; style=&quot;top:0em;&quot;&gt;&lt;span class=&quot;delimsizing size3&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.427em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mclose delimcenter&quot; style=&quot;top:0em;&quot;&gt;&lt;span class=&quot;delimsizing size3&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;/mfrac&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;/mfrac&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{I(\frac{1}{\sqrt{x}})}{2^{23}} - (127 - \sigma) \approx -\frac{1}{2} \cdot \frac{I(x)}{2^{23}} + \frac{1}{2} (127 - \sigma)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.4591em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.7731em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.928em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em;&quot;&gt;&lt;span style=&quot;top:-2.6259em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord sqrt mtight&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8059em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mtight&quot; style=&quot;padding-left:0.833em;&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.7659em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail mtight&quot; style=&quot;min-width:0.853em;height:1.08em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.08em&quot; viewBox=&quot;0 0 400000 1080&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2341em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.538em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.113em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.427em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;I(\frac{1}{\sqrt{x}}) - 2^{23} (127 - \sigma) \approx -\frac{1}{2}  I(x) + \frac{1}{2} (2^{23} (127 - \sigma))&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.2514em;vertical-align:-0.93em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.3097em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord sqrt&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8003em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot; style=&quot;padding-left:0.833em;&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.7603em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail&quot; style=&quot;min-width:0.853em;height:1.08em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.08em&quot; viewBox=&quot;0 0 400000 1080&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2397em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.93em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1141em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;I(\frac{1}{\sqrt{x}}) \approx -\frac{1}{2} I(x) + \frac{3}{2} (2^{23} (127 - \sigma))&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.2514em;vertical-align:-0.93em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.3097em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord sqrt&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8003em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot; style=&quot;padding-left:0.833em;&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.7603em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail&quot; style=&quot;min-width:0.853em;height:1.08em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.08em&quot; viewBox=&quot;0 0 400000 1080&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2397em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.93em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;I(\frac{1}{\sqrt{x}}) \approx \frac{3}{2} (2^{23} (127 - \sigma)) - \frac{1}{2} I(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.2514em;vertical-align:-0.93em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.3097em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord sqrt&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8003em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot; style=&quot;padding-left:0.833em;&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.7603em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail&quot; style=&quot;min-width:0.853em;height:1.08em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.08em&quot; viewBox=&quot;0 0 400000 1080&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2397em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.93em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;msup&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{1}{\sqrt{x}} \approx I^{-1}(\frac{3}{2} (2^{23} (127 - \sigma)) - \frac{1}{2} I(x))&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.2514em;vertical-align:-0.93em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.3097em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord sqrt&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8003em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot; style=&quot;padding-left:0.833em;&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.7603em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail&quot; style=&quot;min-width:0.853em;height:1.08em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.08em&quot; viewBox=&quot;0 0 400000 1080&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2397em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.93em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;h2 id=&quot;the-magic-number&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-magic-number&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The magic number&lt;/h2&gt;
&lt;p&gt;Let’s look at this term:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{3}{2} (2^{23} (127 - \sigma))&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;We know everything here ahead of time. Why don’t we go through and calculate it?&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0.0450466&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\sigma = 0.0450466&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0.0450466&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;23&lt;/mn&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;127&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;0.0450466&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1.5974630065963008&lt;/mn&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msup&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mn&gt;9&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{3}{2} (2^{23} (127 - 0.0450466)) = 1.5974630065963008 \cdot 10^9&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;23&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0.0450466&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1.5974630065963008&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;9&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;1.5974630065963008&lt;/mn&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msup&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mn&gt;9&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mn&gt;1597463007&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;1.5974630065963008 \cdot 10^9 \approx 1597463007&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1.5974630065963008&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;9&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1597463007&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;159746300&lt;/mn&gt;&lt;msub&gt;&lt;mn&gt;7&lt;/mn&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mn&gt;3759&lt;/mn&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mn&gt;16&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;1597463007_{10} = 5F3759DF_{16}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7944em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;159746300&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.13889em;&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;3759&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.02778em;&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.13889em;&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;16&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;So &lt;em&gt;there’s&lt;/em&gt; where that’s from.&lt;/p&gt;
&lt;p&gt;Anyway, we now have:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;msup&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mn&gt;3759&lt;/mn&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mn&gt;16&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{1}{\sqrt{x}} \approx I^{-1}(5F3759DF_{16} - \frac{1}{2} I(x))&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.2514em;vertical-align:-0.93em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.3097em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord sqrt&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8003em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot; style=&quot;padding-left:0.833em;&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.7603em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail&quot; style=&quot;min-width:0.853em;height:1.08em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.08em&quot; viewBox=&quot;0 0 400000 1080&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2397em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.93em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1141em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.13889em;&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;3759&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.02778em;&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.13889em;&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;16&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;This is the gist of how the function works. Let’s step through the code now.&lt;/p&gt;
&lt;h1 id=&quot;the-code&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-code&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Code&lt;/h1&gt;
&lt;p&gt;Let’s go through this code, line by line, to see how it matches up with our mathematical approximation.&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot; data-language=&quot;cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Q_rsqrt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; number &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; x2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; threehalfs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.5F&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	x2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5F&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	y  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	i  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;                       &lt;span class=&quot;token comment&quot;&gt;// evil floating point bit level hacking&lt;/span&gt;
	i  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x5f3759df&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;               &lt;span class=&quot;token comment&quot;&gt;// what the fuck?&lt;/span&gt;
	y  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	y  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; threehalfs &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; x2 &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; y &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// 1st iteration&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//	y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A quick note: the argument to the function is called &lt;code&gt;number&lt;/code&gt;, I’ll be calling it &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;x&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; for simplicity’s sake.&lt;/p&gt;
&lt;h2 id=&quot;evil-floating-point-bit-level-hacking&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#evil-floating-point-bit-level-hacking&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Evil Floating Point Bit Level Hacking&lt;/h2&gt;
&lt;p&gt;Let’s look at the first part of the function.&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot; data-language=&quot;cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Q_rsqrt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; number &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
	y  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	i  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;                       &lt;span class=&quot;token comment&quot;&gt;// evil floating point bit level hacking&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We start by declaring a &lt;code&gt;long&lt;/code&gt;, which is a 32-bit integer, called &lt;code&gt;i&lt;/code&gt;. Then, we declare a &lt;code&gt;float&lt;/code&gt;, or a floating-point representation number, &lt;code&gt;y&lt;/code&gt;. We store the value of the argument (&lt;code&gt;number&lt;/code&gt;, or &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;x&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;) into &lt;code&gt;y&lt;/code&gt;. Simple enough.&lt;/p&gt;
&lt;p&gt;The next line, however, is where things get ugly. Starting from the right, let’s go step-by-step.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;y&lt;/code&gt; is, of course, our floating-point number.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;#x26;y&lt;/code&gt; refers to the &lt;em&gt;reference&lt;/em&gt; to &lt;code&gt;y&lt;/code&gt;—the location in computer memory at which &lt;code&gt;y&lt;/code&gt; is stored. &lt;code&gt;&amp;#x26;y&lt;/code&gt; is a &lt;em&gt;pointer&lt;/em&gt; to a floating-point number.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;( long * )&lt;/code&gt; is a &lt;em&gt;cast&lt;/em&gt;—it converts a value from one type to another. Here, we’re converting &lt;code&gt;&amp;#x26;y&lt;/code&gt; from “pointer to a floating-point number” to “pointer to a 32-bit integer”. This doesn’t modify the bits in &lt;code&gt;y&lt;/code&gt; at all, it modifies the &lt;em&gt;type&lt;/em&gt; of the pointer. It tells the compiler that the &lt;em&gt;value&lt;/em&gt; at this pointer isn’t a float, it’s an int.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;i = * [...]&lt;/code&gt; will &lt;em&gt;dereference&lt;/em&gt; the pointer. It sets &lt;code&gt;i&lt;/code&gt; equal to the value at that pointer. Since the pointer is considered a pointer to a 32-bit integer, and &lt;code&gt;i&lt;/code&gt; is declared as a 32-bit integer, this just sets &lt;code&gt;i&lt;/code&gt; equal to the bits at that location in memory.&lt;/p&gt;
&lt;p&gt;Effectively, this part takes the bits in the floating-point representation of the argument (&lt;code&gt;number&lt;/code&gt;) and interprets them as a 32-bit integer instead.&lt;/p&gt;
&lt;p&gt;Does that sound familiar? It’s our &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;I(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; function we defined earlier! These lines could be written as&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;←&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;y \gets x&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.625em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;←&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;←&lt;/mo&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;i \gets I(y)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6595em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;←&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Or, more concisely:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;←&lt;/mo&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;i \gets I(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6595em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;←&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;h2 id=&quot;whats-with-all-this-memory-trickery&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#whats-with-all-this-memory-trickery&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;What’s with all this memory trickery?&lt;/h2&gt;
&lt;p&gt;You might ask, why can’t we just do this?&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot; data-language=&quot;cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After all, we just want &lt;code&gt;y&lt;/code&gt; as an integer, right?&lt;/p&gt;
&lt;p&gt;However, this expression will actually convert the &lt;em&gt;value&lt;/em&gt; that &lt;code&gt;y&lt;/code&gt; represents into an integer. For instance, if &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;5.2&lt;/mn&gt;&lt;msub&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1.0101&lt;/mn&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;y = 5.25_{10} = 1.0101 \cdot 2^2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.625em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7944em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;5.2&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1.0101&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, then this code will set &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;i = 5&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6595em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. It will &lt;em&gt;convert&lt;/em&gt; &lt;code&gt;y&lt;/code&gt; to an integer.&lt;/p&gt;
&lt;p&gt;This isn’t what we want. We don’t want to do any conversion — we are literally taking the bit representation of &lt;code&gt;y&lt;/code&gt; and interpreting it as an integer instead. Thus, we convert the &lt;em&gt;pointer&lt;/em&gt; instead, which doesn’t actually modify the bits stored in memory.&lt;/p&gt;
&lt;p&gt;You might think that the code looks ugly. That’s because it is—casting a pointer from one type to another is considered “undefined behavior,” and the C standard does not guarantee what will happen. We’re basically tricking the compiler here. This is definitely considered bad practice, which is why it’s &lt;em&gt;“Evil Floating Point Bit Level Hacking”&lt;/em&gt;. &lt;em&gt;Evil&lt;/em&gt; because it’s relying on undefined behavior, &lt;em&gt;Floating Point&lt;/em&gt; because we’re working with a floating-point representation, &lt;em&gt;Bit Level&lt;/em&gt; because we’re directly using the bit representation as an integer, and &lt;em&gt;Hacking&lt;/em&gt; because this is most definitely not the way casting is intended to be used.&lt;/p&gt;
&lt;h2 id=&quot;the-wtf-line&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-wtf-line&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The “WTF” Line&lt;/h2&gt;
&lt;pre class=&quot;language-cpp&quot; data-language=&quot;cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Q_rsqrt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; number &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
	i  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x5f3759df&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;               &lt;span class=&quot;token comment&quot;&gt;// what the fuck?&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you aren’t familiar with bitwise operations, the symbol &lt;code&gt;( i &gt;&gt; 1 )&lt;/code&gt; might seem strange to you.&lt;/p&gt;
&lt;p&gt;Think about doing a division problem like &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;13560&lt;/mn&gt;&lt;mo&gt;÷&lt;/mo&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;13560 \div 3&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;13560&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;÷&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. It would be pretty tough, right? You’d probably need to write out the entire long division problem.&lt;/p&gt;
&lt;p&gt;On the other hand, think about finding &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;13560&lt;/mn&gt;&lt;mo&gt;÷&lt;/mo&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;13560 \div 10&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;13560&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;÷&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. You can pretty quickly tell that the result is &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;1356&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;1356&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;1356&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, right? All you had to do was shift all the digits one place to the right.&lt;/p&gt;
&lt;p&gt;We can use the same trick in binary. To divide a number by 2, we just need to shift each bit to the right. That is what &lt;code&gt;&gt;&gt; 1&lt;/code&gt; does—it’s just a much faster way to divide by two.&lt;/p&gt;
&lt;p&gt;Remember, in the last step, we set &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;←&lt;/mo&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;i \gets I(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6595em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;←&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. Thus, &lt;code&gt;( i &gt;&gt; 1 )&lt;/code&gt; is &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{1}{2} I(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1901em;vertical-align:-0.345em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em;&quot;&gt;&lt;span style=&quot;top:-2.655em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.345em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;We then subtract this from our magic number:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;←&lt;/mo&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mn&gt;3759&lt;/mn&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mn&gt;16&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;i \gets 5F3759DF_{16} - \frac{1}{2} I(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6595em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;←&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.13889em;&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;3759&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.02778em;&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.13889em;&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;16&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;h2 id=&quot;finishing-up&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#finishing-up&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Finishing up&lt;/h2&gt;
&lt;pre class=&quot;language-cpp&quot; data-language=&quot;cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Q_rsqrt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; number &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
	y  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;#x26;&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks very similar to the &lt;code&gt;i = * ( long * ) &amp;#x26;y;&lt;/code&gt; line we looked at earlier, and that’s because it is. However, instead of interpreting the floating-point representation &lt;code&gt;y&lt;/code&gt; as an integer, we’re interpreting the integer &lt;code&gt;i&lt;/code&gt; as a floating-point representation. You can think of this as our &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;I^{-1}(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.0641em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; function.&lt;/p&gt;
&lt;p&gt;This step performs &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;←&lt;/mo&gt;&lt;msup&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;y \gets I^{-1}(i)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.625em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;←&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.0641em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. Since the last step set &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;←&lt;/mo&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mn&gt;3759&lt;/mn&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mn&gt;16&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;i \gets 5F3759DF_{16} - \frac{1}{2} I(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6595em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;←&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.8333em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.13889em;&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;3759&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.02778em;&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.13889em;&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;16&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1901em;vertical-align:-0.345em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em;&quot;&gt;&lt;span style=&quot;top:-2.655em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.345em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, this effectively sets:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;←&lt;/mo&gt;&lt;msup&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mn&gt;3759&lt;/mn&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mn&gt;16&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;y \gets I^{-1}(5F3759DF_{16} - \frac{1}{2} I(x))&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.625em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;←&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1141em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.13889em;&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;3759&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.02778em;&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.13889em;&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;16&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;There we go!&lt;/p&gt;
&lt;h1 id=&quot;but-wait-theres-more-newtons-method&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#but-wait-theres-more-newtons-method&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;But wait, there’s more: Newton’s method&lt;/h1&gt;
&lt;pre class=&quot;language-cpp&quot; data-language=&quot;cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Q_rsqrt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; number &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; x2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; threehalfs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.5F&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	x2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5F&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
	y  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; threehalfs &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; x2 &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; y &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// 1st iteration&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//	y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What does this do?&lt;/p&gt;
&lt;p&gt;This line performs “Newton’s method”, which is a method of refining an approximation for the root of a function.&lt;/p&gt;
&lt;p&gt;Let’s define &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msup&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;/mfrac&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;f(t) = \frac{1}{t^2} - x&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1901em;vertical-align:-0.345em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em;&quot;&gt;&lt;span style=&quot;top:-2.655em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7463em;&quot;&gt;&lt;span style=&quot;top:-2.786em;margin-right:0.0714em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.5em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size3 size1 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.345em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. Notice that when &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;t = \frac{1}{\sqrt{x}}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6151em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.3831em;vertical-align:-0.538em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em;&quot;&gt;&lt;span style=&quot;top:-2.6259em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord sqrt mtight&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8059em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mtight&quot; style=&quot;padding-left:0.833em;&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.7659em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail mtight&quot; style=&quot;min-width:0.853em;height:1.08em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.08em&quot; viewBox=&quot;0 0 400000 1080&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2341em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.538em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;f(t) = 0&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. Therefore, we can use a root-finding algorithm to try to find this root of &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;f(t)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, and we’ll get back a better approximation for &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msqrt&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/msqrt&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;t = \frac{1}{\sqrt{x}}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6151em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.3831em;vertical-align:-0.538em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em;&quot;&gt;&lt;span style=&quot;top:-2.6259em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord sqrt mtight&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8059em;&quot;&gt;&lt;span class=&quot;svg-align&quot; style=&quot;top:-3em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mtight&quot; style=&quot;padding-left:0.833em;&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.7659em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;hide-tail mtight&quot; style=&quot;min-width:0.853em;height:1.08em;&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;400em&quot; height=&quot;1.08em&quot; viewBox=&quot;0 0 400000 1080&quot; preserveAspectRatio=&quot;xMinYMin slice&quot;&gt;&lt;path d=&quot;M95,702
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l0 -0
c5.3,-9.3,12,-14,20,-14
H400000v40H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M834 80h400000v40h-400000z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2341em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.538em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Here’s a graph that shows how Newton’s method works:&lt;/p&gt;
&lt;desmos id=&quot;ibkfxugxss&quot;&gt;
&lt;p&gt;Note: since we’re working with both arguments to &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;Q&lt;/mi&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;_&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;s&lt;/mi&gt;&lt;mi&gt;q&lt;/mi&gt;&lt;mi&gt;r&lt;/mi&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;Q\_rsqrt(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.06em;vertical-align:-0.31em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;mord&quot; style=&quot;margin-right:0.02778em;&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;rs&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.02778em;&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; and arguments to &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;f(t)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, I’ve decided to stick with using &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;t&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6151em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; for the latter. Generally, when working with Newton’s method, this value would be called &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;x&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;We have our function in red, and an initial guess in dotted black, and we’re trying to find the point at which the function crosses the t-axis. We can draw a line tangent to the function at our initial guess (in green) and then find the intersection of that line with the t-axis to get an even better guess. We can keep doing this until we’re happy with the precision.&lt;/p&gt;
&lt;p&gt;So, we have our initial guess given by our &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mn&gt;3759&lt;/mn&gt;&lt;mi&gt;D&lt;/mi&gt;&lt;msub&gt;&lt;mi&gt;F&lt;/mi&gt;&lt;mn&gt;16&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mi&gt;I&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;I^{-1}(5F3759DF_{16} - \frac{1}{2} I(x)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.0641em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.13889em;&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;3759&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.02778em;&quot;&gt;D&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.13889em;&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.1389em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;16&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1901em;vertical-align:-0.345em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em;&quot;&gt;&lt;span style=&quot;top:-2.655em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.345em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.07847em;&quot;&gt;I&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; expression. Let’s call this guess &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;t_0&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7651em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. How do we draw a tangent line?&lt;/p&gt;
&lt;p&gt;Remember, the point-slope form for a line is given by &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;y = m(t - t_0) + y_0&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.625em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.625em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. Thus, we can just plug in our initial point &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;(t_0, f(t_0))&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mpunct&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.1667em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; to get &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;y = m(t - t_0) + f(t_0)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.625em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;To get the slope &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;m&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;m&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, we need to take the derivative of the function &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;f(t)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. Let’s do this later—just call it &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo mathvariant=&quot;normal&quot; lspace=&quot;0em&quot; rspace=&quot;0em&quot;&gt;′&lt;/mo&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;f&apos;(t)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.0019em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7519em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;′&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; for now.&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msup&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo mathvariant=&quot;normal&quot; lspace=&quot;0em&quot; rspace=&quot;0em&quot;&gt;′&lt;/mo&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;y = f&apos;(t) \cdot (t - t_0) + f(t_0)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.625em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.0519em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8019em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;′&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;We’re trying to find the point where this tangent line crosses the t-axis, that is, where &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;y = 0&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.625em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. Substitute &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;0&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; for &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;y&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.625em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msup&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo mathvariant=&quot;normal&quot; lspace=&quot;0em&quot; rspace=&quot;0em&quot;&gt;′&lt;/mo&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;0 = f&apos;(t) \cdot (t - t_0) + f(t_0)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.0519em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8019em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;′&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msup&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo mathvariant=&quot;normal&quot; lspace=&quot;0em&quot; rspace=&quot;0em&quot;&gt;′&lt;/mo&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;f(t_0) = - f&apos;(t) \cdot (t - t_0)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.0519em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8019em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;′&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo mathvariant=&quot;normal&quot; lspace=&quot;0em&quot; rspace=&quot;0em&quot;&gt;′&lt;/mo&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;-\frac{f(t_0)}{f&apos;(t_0)} = t - t_0&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.363em;vertical-align:-0.936em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.427em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6779em;&quot;&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;′&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.936em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6984em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7651em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo mathvariant=&quot;normal&quot; lspace=&quot;0em&quot; rspace=&quot;0em&quot;&gt;′&lt;/mo&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;t = t_0 - \frac{f(t_0)}{f&apos;(t_0)}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6151em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7651em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.363em;vertical-align:-0.936em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.427em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6779em;&quot;&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;′&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.936em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;And we’ve arrived at the formula for Newton’s method.&lt;/p&gt;
&lt;p&gt;Let’s substitute in our &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;f(t)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; and &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo mathvariant=&quot;normal&quot; lspace=&quot;0em&quot; rspace=&quot;0em&quot;&gt;′&lt;/mo&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;f&apos;(t)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.0019em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7519em;&quot;&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;′&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. First, we need to find the derivative:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo mathvariant=&quot;normal&quot; lspace=&quot;0em&quot; rspace=&quot;0em&quot;&gt;′&lt;/mo&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mi&gt;d&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;d&lt;/mi&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mo fence=&quot;false&quot; stretchy=&quot;true&quot; minsize=&quot;1.8em&quot; maxsize=&quot;1.8em&quot;&gt;[&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msup&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;/mfrac&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo fence=&quot;false&quot; stretchy=&quot;true&quot; minsize=&quot;1.8em&quot; maxsize=&quot;1.8em&quot;&gt;]&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mi&gt;d&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;d&lt;/mi&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;msup&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;msup&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;f&apos;(t) = \frac{d}{dt} \Big[ \frac{1}{t^2} - x \Big] = \frac{d}{dt} t^{-2} = -2t^{-3}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.0519em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8019em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;′&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0574em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3714em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;d&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;delimsizing size2&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7401em;&quot;&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.8em;vertical-align:-0.65em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;delimsizing size2&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0574em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3714em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;d&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.9474em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord mtight&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Now, proceed:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mi&gt;f&lt;/mi&gt;&lt;mo mathvariant=&quot;normal&quot; lspace=&quot;0em&quot; rspace=&quot;0em&quot;&gt;′&lt;/mo&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;t = t_0 - \frac{f(t_0)}{f&apos;(t_0)}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6151em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7651em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.363em;vertical-align:-0.936em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.427em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6779em;&quot;&gt;&lt;span style=&quot;top:-2.989em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;′&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.10764em;&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.936em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msubsup&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;/mfrac&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;msubsup&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/mrow&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;t = t_0 - \frac{\frac{1}{t_0^2} - x}{-2t_0^{-3}}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6151em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7651em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.8373em;vertical-align:-1.0105em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.8267em;&quot;&gt;&lt;span style=&quot;top:-2.2558em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8542em;&quot;&gt;&lt;span style=&quot;top:-2.4337em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.1031em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord mtight&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2663em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.9816em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em;&quot;&gt;&lt;span style=&quot;top:-2.6264em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8051em;&quot;&gt;&lt;span style=&quot;top:-2.1885em;margin-left:0em;margin-right:0.0714em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.5em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size3 size1 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.8448em;margin-right:0.0714em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.5em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size3 size1 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3115em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.5916em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.0105em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;msubsup&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;/mfrac&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;msubsup&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/msubsup&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;t = t_0 - \frac{(\frac{1}{t_0^2} - x)t_0^3}{-2}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6151em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7651em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.5961em;vertical-align:-0.7693em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.8267em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.9816em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8451em;&quot;&gt;&lt;span style=&quot;top:-2.6264em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8051em;&quot;&gt;&lt;span style=&quot;top:-2.1885em;margin-left:0em;margin-right:0.0714em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.5em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size3 size1 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-2.8448em;margin-right:0.0714em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.5em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size3 size1 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3115em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.5916em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-2.4519em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2481em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.7693em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msubsup&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/msubsup&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;t = t_0 + \frac{t_0 - t_0^3x}{2}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6151em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7651em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.1771em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.4911em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-2.4519em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2481em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msubsup&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/msubsup&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;t = \frac{2t_0 + t_0 - t_0^3x}{2}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6151em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.1771em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.4911em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-2.4519em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2481em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msubsup&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;/msubsup&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;t = \frac{3t_0 - t_0^3x}{2}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6151em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.1771em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.4911em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-2.4519em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2481em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msubsup&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;t = \frac{t_0(3 - t_0^2x)}{2}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6151em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.1771em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.4911em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-2.4519em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2481em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;msubsup&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;t = t_0 \cdot \frac{3 - t_0^2x}{2}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6151em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7651em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.1771em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.4911em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-2.4519em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2481em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;msubsup&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;t = t_0 ( \frac{3}{2} - \frac{t_0^2x}{2} )&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6151em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.1771em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.4911em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8141em;&quot;&gt;&lt;span style=&quot;top:-2.4519em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.063em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.2481em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/msub&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;msubsup&gt;&lt;mi&gt;t&lt;/mi&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msubsup&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;t = t_0 ( \frac{3}{2} - \frac{x}{2} t_0^2 )&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6151em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.7936em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.1076em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-2.453em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.247em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Let’s look at that line of code again:&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot; data-language=&quot;cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Q_rsqrt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; number &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; x2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; threehalfs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.5F&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	x2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5F&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
	y  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; threehalfs &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; x2 &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; y &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// 1st iteration&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//	y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Earlier in the code, there is a line that performs &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;←&lt;/mo&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mn&gt;0.5&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;x_2 \gets x \cdot 0.5&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.5806em;vertical-align:-0.15em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;←&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4445em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0.5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. This is just so that &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\frac{x}{2}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.0404em;vertical-align:-0.345em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.6954em;&quot;&gt;&lt;span style=&quot;top:-2.655em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.394em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.345em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; doesn’t have to be recalculated on each iteration (even though the second iteration was since removed). Note that we multiply by 0.5 instead of dividing by 2 because multiplication is faster than division. Also, we can’t do the bit-shifting trick here since &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;x&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; is a floating-point number, not an integer.&lt;/p&gt;
&lt;p&gt;The Newton’s iteration lines do:&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;←&lt;/mo&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msub&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msub&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;y \gets y (\frac{3}{2} - ( x_2 \cdot y \cdot y ))&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.625em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;←&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.3011em;&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:0em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.15em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6389em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;⋅&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;which is equivalent to&lt;/p&gt;
&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo&gt;←&lt;/mo&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mfrac&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mfrac&gt;&lt;mi&gt;x&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;msup&gt;&lt;mi&gt;y&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;y \gets y (\frac{3}{2} - \frac{x}{2} y^2 )&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.625em;vertical-align:-0.1944em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;←&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:2.0074em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.3214em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;−&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.7936em;vertical-align:-0.686em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mopen nulldelimiter&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mfrac&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:1.1076em;&quot;&gt;&lt;span style=&quot;top:-2.314em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.23em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;frac-line&quot; style=&quot;border-bottom-width:0.04em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.677em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;x&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.686em;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose nulldelimiter&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:0.8641em;&quot;&gt;&lt;span style=&quot;top:-3.113em;margin-right:0.05em;&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;sizing reset-size6 size3 mtight&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mclose&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;which is exactly the expression for Newton’s method we found earlier.&lt;/p&gt;
&lt;p&gt;This line of code can be repeated to get better and better approximations. However, it appears the authors of Quake III decided that only one iteration was necessary, since the second one was removed.&lt;/p&gt;
&lt;h3 id=&quot;the-end&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-end&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The End&lt;/h3&gt;
&lt;pre class=&quot;language-cpp&quot; data-language=&quot;cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Q_rsqrt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; number &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’ll end with a quote from a relevant &lt;a href=&quot;https://xkcd.com/664/&quot;&gt;xkcd&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Some engineer out there has solved P=NP and it’s locked up in an electric eggbeater calibration routine. For every 0x5f375a86 we learn about, there are thousands we never see.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;(It looks like the constant Randall mentioned was based on &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0.0430357&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\sigma = 0.0430357&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0.0430357&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;, not &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;σ&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0.0450466&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;\sigma = 0.0450466&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4306em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:0.03588em;&quot;&gt;σ&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0.0450466&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.)&lt;/p&gt;&lt;/desmos&gt;&lt;/desmos&gt;</content:encoded></item><item><title>How I Use Dokku</title><link>https://breq.dev/2021/02/10/dokku/</link><guid isPermaLink="true">https://breq.dev/2021/02/10/dokku/</guid><description>A one-stop-shop for building, deploying, and hosting apps on a budget.</description><pubDate>Wed, 10 Feb 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As I’ve started to explore cloud and microservices-based projects, I’ve turned to Dokku to host and manage my running projects. It’s become more or less my one-stop-shop for hosting all the projects I’ve worked on. A lot of my projects have different requirements, though, so I wanted to share the techniques and setup I use to keep everything running smoothly. This isn’t an exhaustive list of all the projects I host, but it chronicles the difficulties I’ve had over time.&lt;/p&gt;
&lt;p&gt;This isn’t going to be a How-To guide for Dokku—there are plenty of those already. It’s more of a collection of tips and tricks, and an explanation of which features I find most useful.&lt;/p&gt;
&lt;h1 id=&quot;what-is-dokku&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#what-is-dokku&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;What is Dokku?&lt;/h1&gt;
&lt;p&gt;With the growth in popularity of &lt;a href=&quot;https://en.wikipedia.org/wiki/Microservices&quot;&gt;microservices&lt;/a&gt;, there has also been a growth in “Platform-as-a-Service” providers like &lt;a href=&quot;https://www.heroku.com/&quot;&gt;Heroku&lt;/a&gt;. These platforms abstract away the details of running a Linux VM and installing dependencies, which drastically simplifies the process of deploying apps. However, these services can be expensive—for instance, once you burn through Heroku’s free tier, you’ll be charged $7 per month &lt;em&gt;per container&lt;/em&gt;. This might be a fair price for businesses who need solid reliability and high performance, but it’s prohibitively expensive for a tinkerer who just wants to try out a new technology or work on a side project every so often.&lt;/p&gt;
&lt;p&gt;Dokku is a &lt;em&gt;self-hosted&lt;/em&gt; platform-as-a-service, and it can be installed on any Linux machine you have access to. It’s limited to only one machine, so all of your apps will need to fight over the system’s CPU/ram/etc, and there’s no easy way to scale across multiple servers. However, renting a single server is much cheaper than running many different containers on a retail PaaS. At time of writing, I’m running about 15 different projects, which would cost about &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;7&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;15&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mo&gt;∗&lt;/mo&gt;&lt;mo&gt;∗&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;7×15 = **&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.7278em;vertical-align:-0.0833em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;×&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2222em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.6444em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:0.2778em;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:0.4653em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;∗&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;∗&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;105** every month. That’s a lot more expensive than a $5 VPS.&lt;/p&gt;
&lt;h1 id=&quot;the-server&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-server&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Server&lt;/h1&gt;
&lt;p&gt;My Dokku instance is currently being rented from Azure with credits I’ve been getting for free (it’s a long story). I run Ubuntu LTS because it’s stable, popular, and what I’m familiar with. I just used Dokku’s &lt;code&gt;bootstrap.sh&lt;/code&gt; script to get started. Their &lt;a href=&quot;http://dokku.viewdocs.io/dokku/&quot;&gt;docs&lt;/a&gt; have a good installation guide.&lt;/p&gt;
&lt;p&gt;I use Dokku’s &lt;a href=&quot;https://github.com/dokku/dokku-letsencrypt&quot;&gt;letsencrypt plugin&lt;/a&gt; to manage HTTPS for all the apps. Handling this at the platform layer instead of the application layer makes applications easier to develop—I don’t have to worry about encryption for every single app, I can just configure it once in Dokku. Encryption is a necessity for me, as my website runs on a &lt;code&gt;.dev&lt;/code&gt; domain and is thus on the HSTS preload list by default. (I’m glad that Google did this, and I’m happy to be part of the push for HTTPS adoption everywhere, but &lt;em&gt;boy&lt;/em&gt; is working around it frustrating sometimes.)&lt;/p&gt;
&lt;h1 id=&quot;snowflake&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#snowflake&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Snowflake&lt;/h1&gt;
&lt;p&gt;I’ve written about Snowflake &lt;a href=&quot;https://breq.dev/projects/snowflake&quot;&gt;here&lt;/a&gt; before, but here’s the TL;DR: It’s a service for generating unique, time-ordered, 64-bit ID numbers. It’s built with Python and Flask.&lt;/p&gt;
&lt;p&gt;Notably, it’s segmented into two distinct parts: “Snowflake” generates the IDs based on its instance number, and “Snowcloud” assigns the instance numbers to the Snowflake instances. This is for scalability reasons: it’s possible to scale the Snowflake app to many different instances, but as long as each has a unique instance number, the IDs will not conflict. Additionally, Redis is used to keep track of the in-use instance numbers.&lt;/p&gt;
&lt;p&gt;Deploying Flask apps to Dokku is easy—no special buildpacks are required, and the Procfile is simply &lt;code&gt;web: gunicorn app:app&lt;/code&gt;. Adding Redis is also pretty straightforward using Dokku’s &lt;a href=&quot;https://github.com/dokku/dokku-redis&quot;&gt;official Redis plugin&lt;/a&gt;. When you create a Redis instance and link it to your app, Dokku will set the &lt;code&gt;REDIS_URL&lt;/code&gt; environment variable in your app to point to that Redis instance. For testing locally, I just put &lt;code&gt;export REDIS_URL=redis://localhost:6379&lt;/code&gt; in my &lt;code&gt;.env&lt;/code&gt; so that my test and production environments are similar.&lt;/p&gt;
&lt;p&gt;The Snowcloud app will only assign instance IDs to processes with the proper API key. Thus, this key needs to be set in both the Snowcloud and Snowflake app. The &lt;code&gt;dokku config&lt;/code&gt; command is your friend here: I just set them as environment variables using &lt;code&gt;dokku config:set SNOWCLOUD-KEY={key}&lt;/code&gt;. Being able to set secrets like this in environment variables is really handy—it’s great to keep them out of the repo (especially because I like to share my code on my GitHub), and it’s a lot easier than trying to store them in a file or something.&lt;/p&gt;
&lt;h1 id=&quot;autoredditor&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#autoredditor&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;AutoRedditor&lt;/h1&gt;
&lt;p&gt;AutoRedditor is a project I made to quickly return random Reddit posts on demand. It has two main parts: a worker thread to retrieve Reddit posts and store them in Redis, and a web thread to retrieve posts from Redis when a request is received. It’s built with Python, but using &lt;a href=&quot;https://docs.python.org/3/library/asyncio.html&quot;&gt;asyncio&lt;/a&gt;, mostly because I wanted an excuse to learn the technology.&lt;/p&gt;
&lt;p&gt;Running the worker thread was as simple as adding &lt;code&gt;worker: python3 worker.py&lt;/code&gt; to the Procfile. However, running the web thread was a bit more difficult, as I’m using &lt;a href=&quot;https://pgjones.gitlab.io/quart/&quot;&gt;Quart&lt;/a&gt; instead of Flask here. Typically, deploying a Quart app is just as simple as replacing Gunicorn with Hypercorn, but it’s a bit trickier here. Dokku apps need to respect the &lt;code&gt;PORT&lt;/code&gt; environment variable and make their web interface available on that port. The solution is to explicitly specify the port variable in the command: &lt;code&gt;web: hypercorn -b 0.0.0.0:${PORT} app:app&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id=&quot;cards&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#cards&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Cards&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://breq.dev/projects/cards&quot;&gt;Cards&lt;/a&gt; is a service to generate image or HTML-based cards for embedding into websites. It uses &lt;a href=&quot;https://github.com/pyppeteer/pyppeteer&quot;&gt;Pyppeteer&lt;/a&gt;, a Python port of the popular Puppeteer JS library used for automating actions in a headless web browser. Specifically, Pyppeteer is used to render and screenshot HTML-based cards in order to produce images. (I experimented with using Selenium, but I found Pyppeteer was easier to install and use.) Because Pyppeteer is based on asyncio, I decided to go with Quart (+ Hypercorn) for this project as well. Getting Pyppeteer to run in an app container isn’t particularly straightforward, as it requires a Chromium install to use for the browser.&lt;/p&gt;
&lt;p&gt;Thankfully, Heroku has published a &lt;a href=&quot;https://github.com/heroku/heroku-buildpack-google-chrome&quot;&gt;Google Chrome buildpack&lt;/a&gt; that can be used to install Chrome into the app. Running &lt;code&gt;dokku buildpacks:add cards https://github.com/heroku/heroku-buildpack-google-chrome&lt;/code&gt; and &lt;code&gt;dokku buildpacks:add cards https://github.com/heroku/heroku-buildpack-python&lt;/code&gt; will configure the app to install both Chrome and a Python runtime. Heroku’s buildpack sets the &lt;code&gt;GOOGLE_CHROME_SHIM&lt;/code&gt; environment variable, and this just has to be passed to Pyppeteer’s &lt;code&gt;launch(executablePath)&lt;/code&gt; function. For local testing, leave this variable unset, and Pyppeteer will just use your local Chrome install.&lt;/p&gt;
&lt;p&gt;Cards will also cache the screenshots so that it doesn’t have to run Pyppeteer for every request. To do this, I needed to mount some sort of persistent storage to the running container. The &lt;code&gt;dokku docker-options&lt;/code&gt; command was perfect for this: I just needed to add &lt;code&gt;-v /home/breq/cards:/storage&lt;/code&gt; to the deploy options.&lt;/p&gt;
&lt;h1 id=&quot;emoji&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#emoji&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Emoji&lt;/h1&gt;
&lt;p&gt;I made a simple emoji keyboard at &lt;a href=&quot;https://emoji.breq.dev/&quot;&gt;emoji.breq.dev&lt;/a&gt; because I was frustrated that I couldn’t send emoji from my computer with Google Voice. I used Jekyll, which is overkill for a single-page site, but I wanted to use my existing website theme and avoid repeating the same code over and over for every single emoji. I wanted to keep my built &lt;code&gt;_site&lt;/code&gt; folder out of the repo to avoid cluttering things up, which complicated Dokku deployment a bit.&lt;/p&gt;
&lt;p&gt;I needed to find a way to make Dokku build the site when I deployed. I found some buildpacks that worked: Heroku’s &lt;a href=&quot;https://github.com/heroku/heroku-buildpack-nginx.git&quot;&gt;nginx pack&lt;/a&gt; and &lt;a href=&quot;https://github.com/inket/dokku-buildpack-jekyll3-nginx.git&quot;&gt;inket’s Jekyll pack&lt;/a&gt;. These were surprisingly painless—the Jekyll pack tells Dokku how to install Ruby, run Jekyll to build the site, and point nginx to the &lt;code&gt;_site&lt;/code&gt; folder.&lt;/p&gt;
&lt;h1 id=&quot;breqbot&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#breqbot&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Breqbot&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://breq.dev/projects/breqbot&quot;&gt;Breqbot&lt;/a&gt; is a Discord bot I built. It uses the traditional Gateway API instead of the newer Interactions one, so most functions are handled by a worker thread that connects to Discord over a WebSocket. However, some information is available over a REST API, so there’s a web thread that runs as well.&lt;/p&gt;
&lt;p&gt;The most difficult part about getting Breqbot to work was the voice features. Breqbot includes a soundboard feature to play sound in a Discord voice channel. Handling these audio codecs requires installing quite a few packages: I ended up using &lt;a href=&quot;https://github.com/heroku/heroku-buildpack-apt.git&quot;&gt;Heroku’s Apt buildpack&lt;/a&gt; to install &lt;code&gt;libffi-dev&lt;/code&gt;, &lt;code&gt;libnacl-dev&lt;/code&gt;, and &lt;code&gt;libopus-dev&lt;/code&gt;, and I used &lt;a href=&quot;https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest.git&quot;&gt;jonathanong’s ffmpeg buildpack&lt;/a&gt; for, well, ffmpeg.&lt;/p&gt;
&lt;h1 id=&quot;minecraft&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#minecraft&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Minecraft&lt;/h1&gt;
&lt;p&gt;I wanted to host a Minecraft server on my cloud VPS as well, so I decided to try to use Dokku to manage it. So far, it’s been working out pretty well, and it’s nice to have everything managed in one place. However, getting Minecraft to work initially in a containerized setup wasn’t straightforward.&lt;/p&gt;
&lt;p&gt;I found &lt;a href=&quot;https://github.com/itzg/docker-minecraft-server/&quot;&gt;itzg’s Docker Minecraft server image&lt;/a&gt; and set about making it work with Dokku. Dokku does support deploying from a Docker image, and although the process isn’t particularly straightforward, it is at least &lt;a href=&quot;http://dokku.viewdocs.io/dokku/deployment/methods/images/#deploying-from-a-docker-registry&quot;&gt;well documented&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next, I set the docker options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-e TYPE=PAPER&lt;/code&gt;, to run a high-performance Paper server instead of the default vanilla one&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p 25565:25565&lt;/code&gt;, to expose the Minecraft server port to the Dokku host’s public IP address&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-v /home/breq/minecraft:/data&lt;/code&gt;, to configure a persistant place to store the world data, plugins, datapacks, etc&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, I needed a way to access the server console to run commands while the server is running. I found &lt;a href=&quot;https://github.com/mesacarlos/WebConsole&quot;&gt;mesacarlos’ WebConsole plugin&lt;/a&gt;, which can provide a password-protected console over the Internet. To expose this console, I used &lt;code&gt;dokku proxy&lt;/code&gt; to proxy ports 80 and 443 on the host to port 8080 inside the container. I’m currently hosting the web interface in a separate Dokku app. I just made sure to set the WebConsole port to 443 in the interface to connect to the container using HTTPS.&lt;/p&gt;
&lt;h1 id=&quot;syncthing&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#syncthing&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Syncthing&lt;/h1&gt;
&lt;p&gt;I run a &lt;a href=&quot;https://syncthing.net/&quot;&gt;Syncthing&lt;/a&gt; instance to sync files between my desktop and laptop, so I decided to try to run this through Dokku as well. Syncthing provides an official Docker image, so I didn’t have to use a third-party one, and they have good &lt;a href=&quot;https://github.com/syncthing/syncthing/blob/main/README-Docker.md&quot;&gt;documentation&lt;/a&gt; as well. The only change I made from the guide was to proxy the web GUI through Dokku with &lt;code&gt;dokku proxy:ports-add syncthing https:443:8384&lt;/code&gt; instead of exposing it directly to the Internet.&lt;/p&gt;
&lt;h1 id=&quot;wireguard&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#wireguard&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Wireguard&lt;/h1&gt;
&lt;p&gt;I also decided to run a VPN, and I chose Wireguard because it seemed simple, well-supported, and lightweight. The &lt;a href=&quot;https://linuxserver.io&quot;&gt;LinuxServer.io&lt;/a&gt; team maintains a Wireguard &lt;a href=&quot;https://hub.docker.com/r/linuxserver/wireguard&quot;&gt;image&lt;/a&gt;, so I just needed to deploy it to a Dokku app. Using the documentation as a reference, I set these docker options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--cap-add=NET_ADMIN --cap-add=SYS_MODULE&lt;/code&gt; as Wireguard runs as a Linux kernel module, which can’t be containerized, so access to the kernel modules and network have to be granted&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e TZ=America/New_York&lt;/code&gt; to set the timezone&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e SERVERURL=vpn.breq.dev&lt;/code&gt; to set the server URL&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p 51820:51820/udp&lt;/code&gt; to expose the VPN port to the Internet&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-v /home/breq/vpn:/config&lt;/code&gt; to mount the configuration files as a volume—this lets you grab the config files to distribute to the peer computers&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-v /lib/modules:/lib/modules&lt;/code&gt; to mount the kernel modules directory, as Wireguard runs as a Linux kernel module&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--sysctl=net.ipv4.conf.all.src_valid_mark=1&lt;/code&gt; to allow routing all traffic through the VPN&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e PEERS=breq-desk,breq-laptop,breq-phone&lt;/code&gt; to define the peers I want to connect&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;conclusion&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#conclusion&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Running everything all in one place has really simplified things a lot. Being able to deploy apps quickly is really nice for prototyping ideas. Overall, I’m really glad I decided to start using Dokku to manage all the services I’m hosting in the cloud.&lt;/p&gt;</content:encoded></item><item><title>Snowflake</title><link>https://breq.dev/projects/snowflake/</link><guid isPermaLink="true">https://breq.dev/projects/snowflake/</guid><description>A scalable service to generate time-ordered, unique ID numbers.</description><pubDate>Fri, 04 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;This is a service to generate Snowflake IDs. These are unique, time-ordered IDs, useful for things like instant messages or posts.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I wanted to get more experience working with the &lt;a href=&quot;https://12factor.net/&quot;&gt;12 Factor App&lt;/a&gt; model. Implementing a basic service like this seemed like the easiest way to go about it. I also figured this kind of service could be useful for me at some point.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The “Snowflake” ID format was designed at Twitter for identifying tweets, so its scalability is its main selling point. Each Snowflake is a 64-bit integer composed of a timestamp in milliseconds (42 bits), worker ID (10 bits), and increment (12 bits).&lt;/p&gt;
&lt;p&gt;The format can support 1024 possible worker IDs — too many to hardcode by hand, but too few to assign randomly. So, I made a second app, “SnowCloud,” that handles the assignment of these worker IDs to the Snowflake server instances. When a Snowflake server comes online, it will request a worker ID from a SnowCloud server. Then, it will periodically renew the worker ID with the SnowCloud server. The pool of worker IDs are stored as a Redis set, sorted by how recently each was used. In addition, each Snowflake server is identified by a UUID, and these UUIDs are tracked to ensure Snowflake servers can only renew their own assignments. (For reference, the SnowCloud repo is at, predictably, &lt;a href=&quot;https://github.com/breqdev/snowcloud&quot;&gt;breqdev/snowcloud&lt;/a&gt;.)&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;It does everything I set out to accomplish, and I got more experience developing microservices, which was cool. This was just a quick afternoon project—it was cool to go from idea to finished product in just a couple hours.&lt;/p&gt;</content:encoded></item><item><title>BlockChat</title><link>https://breq.dev/projects/blockchat/</link><guid isPermaLink="true">https://breq.dev/projects/blockchat/</guid><description>A simple Blockchain-based chat app.</description><pubDate>Fri, 09 Oct 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1203&quot; height=&quot;751&quot; src=&quot;https://breq.dev/_astro/blockchat.Cl1kcUdO_Z289dNB.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Description&lt;/h1&gt;
&lt;p&gt;BlockChat is a simple decentralized chatroom application that uses a blockchain to store the message data.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;This project was something I quickly threw together in order to better understand blockchain while the Bitcoin boom was taking place. I couldn’t find a resource that explained proof-of-work, consensus, and other blockchain-related topics well enough for me to understand them, so I decided to try making my own blockchain application, loosely based around a couple of examples I saw around the Internet.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;At its core, a blockchain is a distributed, public ledger, so I decided that the easiest blockchain application to write would be a chatroom. At the time, it seemed much easier than a digital token or currency system.&lt;/p&gt;
&lt;p&gt;I started by implementing the blockchain data structure. As the name implies, this is a chain of blocks, where each new block contains the cryptographic hash of the previous block, so that old data cannot be modified without also re-writing all of the new data.&lt;/p&gt;
&lt;p&gt;Then, I moved on to the proof-of-work algorithm. This algorithm is designed to be computationally expensive so that new blocks take a significant amount of time to “mine”. Because of this, if somebody wanted to re-write a previous part of the blockchain, it would take a prohibitively large amount of computing power to then re-write all of the blocks that follow.&lt;/p&gt;
&lt;p&gt;For my proof-of-work algorithm, I decided to require finding an integer with a hash that starts with a certain number. Solutions to this are difficult to find (as miners have to blindly guess at what the number is) but easy to verify (just hash the potential number and see if the first digits match). To make sure no miner tries to re-use the same solution multiple times, I required each new solution to be a larger number than the previous one.&lt;/p&gt;
&lt;p&gt;I didn’t put too much effort into optimizing my miner implementation - it only uses a single CPU thread, sequentially searching numbers. In an actual blockchain scenario, this isn’t a good choice, as anyone who could write a better miner (by using multiple CPU cores, GPUs, or even FPGAs) would then have much more computational power compared to the rest of the network. I also didn’t make the difficulty of the proof-of-work change in response to miner availability. This also isn’t a good idea for a real blockchain, as periods of low miner activity may not be able to mine new blocks quickly. Worse, if many more miners decided to mine my blockchain, a too-easy proof-of-work would make it easier for malicious miners to modify the blockchain.&lt;/p&gt;
&lt;p&gt;Next, I worked on the consensus algorithm. For a node to accept new blocks, it first needs to verify the proof-of-work values and hashes to ensure the chain it receives is valid. Assuming it is, the node needs to choose which version of the chain it should prefer. In my implementation, the node will simply choose the longest version.&lt;/p&gt;
&lt;p&gt;Finally, I needed to devise the protocol which would allow nodes to add messages to the blockchain and compare versions with other nodes. I decided to use a REST API, because it was the easiest solution.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;In the end, the program did work. However, there were a few corners I cut that made the end result kind of impractical.&lt;/p&gt;
&lt;p&gt;The first issue was that I did not include distinction between nodes and miners. In actual blockchains, nodes that want to put data on the blockchain will send that data to many different miners, so that their data would likely be included in the next block, regardless of which miner mined it. In my demo, in order for anybody to send a message to the chatroom, they have to be the one to mine the next block. If the blockchain is scaled beyond just a few miners, the chances of this happening would be nearly zero, so actually using the chatroom would be almost impossible.&lt;/p&gt;
&lt;p&gt;The second issue is the lack of reward for miners. Initially, this was one of the things I didn’t intend to include, because I wanted to keep things simple. But because the miners have no incentive to mine blocks, it would be difficult to get benevolent miners to participate in the blockchain, and it would be easy for “evil” miners to gain enough of a share of the computing power to undermine the stability provided by proof-of-work.&lt;/p&gt;
&lt;p&gt;While the end result wasn’t something practical to deploy, working on this project definitely helped me understand the underlying design of blockchain platforms. It also answered many questions I had about the technology, such as “Why do non-currency applications like &lt;a href=&quot;https://en.wikipedia.org/wiki/Namecoin&quot;&gt;Namecoin&lt;/a&gt; still have tradeable tokens?” and “How exactly does proof-of-work ensure the stability of the blockchain system?”, so I would definitely consider it a success.&lt;/p&gt;</content:encoded></item><item><title>Pinewood Derby Car</title><link>https://breq.dev/projects/pinewood-derby-car/</link><guid isPermaLink="true">https://breq.dev/projects/pinewood-derby-car/</guid><description>A model car full of blinkenlights that I programmed when I was a smol child.</description><pubDate>Fri, 09 Oct 2020 00:00:00 GMT</pubDate><content:encoded>&lt;iframe src=&quot;https://www.youtube.com/embed/X23kVwWfueI?si=s_ofJ9itNmFts5d6&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; class=&quot;w-full aspect-video rounded-2xl overflow-clip&quot;&gt;&lt;/iframe&gt;
&lt;p class=&quot;caption&quot;&gt;
A quick video of the car in action.
&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;When I was a smol child in cub scouts, we had a competition to see who could build the fastest car. I didn’t really care about that, so I decided to try and make something cool instead using an Arduino I had recently gotten.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;Almost everything is wired directly up to the Arduino, except the LED matrix, which runs off of the I2C bus. I modified some example code to display an animation of flames scrolling across (that I had drawn up on some graph paper).&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;I think I won an award for it? (To be honest it’s been so long that I can’t remember well). But I do remember the battery dying - I had used a small garage-door-opener battery in order to fit within the weight limit, and that ended up dying pretty quickly.&lt;/p&gt;</content:encoded></item><item><title>STMusic</title><link>https://breq.dev/projects/stmusic/</link><guid isPermaLink="true">https://breq.dev/projects/stmusic/</guid><description>A Guitar-Hero-style game built for the ST Discovery board.</description><pubDate>Fri, 09 Oct 2020 00:00:00 GMT</pubDate><content:encoded>&lt;iframe src=&quot;https://www.youtube.com/embed/_AXSp7ZT-E8?si=s_ofJ9itNmFts5d6&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; class=&quot;w-full aspect-video rounded-2xl overflow-clip&quot;&gt;&lt;/iframe&gt;
&lt;p class=&quot;caption&quot;&gt;
Here&apos;s a video I recorded demonstrating the game.
&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;This game is similar to Guitar Hero or other rhythm-based games. The player should push the “USER” button whenever one of the scrolling indicators reaches the left of the screen. This was the first major project I made using C. The biggest challenge in this project was figuring out how to store and generate the sounds that made up each song.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;This was a homework assignment in my Computer Organization class to help us learn how to use bitwise operations to display a game on the ST Discovery’s LCD. I chose to add sound functionality mostly because I thought it would be an interesting challenge, I was interested in how basic microcontrollers generate different sounds, and I figured a silent game would be rather boring.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;h2 id=&quot;sound-generation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#sound-generation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Sound Generation&lt;/h2&gt;
&lt;p&gt;The STM32 microcontroller this project used doesn’t have any purpose-built hardware for generating sounds (that I’m aware of). So, the solution I settled on was to manually generate a square wave by setting a GPIO pin high, waiting for half the length of the waveform, setting it low, and waiting for the rest of the waveform.&lt;/p&gt;
&lt;p&gt;The biggest hurdle with this approach was accurate timing. The STM32 can use interrupts to delay for a precise number of milliseconds, but generating square waves at specific frequencies requires sub-millisecond precision. The solution I came up with was to calibrate a busy-wait loop when the code begins using the millisecond timer, then use that busy-wait loop for sub-millisecond-precision delays.&lt;/p&gt;
&lt;p&gt;This yielded a decent-sounding square wave, but the game audio still felt incomplete.&lt;/p&gt;
&lt;p&gt;I attempted to play multiple notes at once by summing the square waves, but the result did not sound very good. Additionally, the timing code required to play two separate frequencies at once quickly became complicated. Perhaps I could have used two separate GPIO pins and a voltage divider to effectively increase the bit depth (allowing for 4 separate voltage levels to be sent to the speaker).&lt;/p&gt;
&lt;p&gt;Instead of attempting that, I decided to try adding drum sounds. By playing each drum sound and then quickly switching to playing the melodic note, the device can give the illusion that both sounds are playing at once. This didn’t work out as well as I had hoped, but it sounded okay at least.&lt;/p&gt;
&lt;p&gt;For the kick drum, I borrowed a &lt;a href=&quot;https://www.youtube.com/watch?v=Jd6nyynuzio&quot;&gt;trick&lt;/a&gt; used frequently by composers for the NES: By doing a rapid downward pitch bend in between melodic sections, it’s possible to fake a kick drum sound somewhat convincingly. Because I don’t have the luxury of a triangle-wave channel, this doesn’t sound as good in my project as it does in NES games, but the trick still works.&lt;/p&gt;
&lt;p&gt;For the snare drum, I decided to just use a burst of white noise. But as the STM32 doesn’t have any built-in random number generation, I had to choose a pseudorandom algorithm to implement.&lt;/p&gt;
&lt;p&gt;At first, I tried to use a Linear Congruential Generator, because it seemed easier to implement. However, with the parameters I chose, the period was small enough that I could hear a distinct tone in the output. I could have probably eliminated this by choosing better parameters, but I didn’t want to spent a bunch of time tuning the parameters.&lt;/p&gt;
&lt;p&gt;I then looked into using a Mersenne Twister, because it seemed like a popular choice. I ultimately decided it against it as it seemed hard to implement. I also worried that it might be too slow, considering I’d want to be sending bits to the GPIO pin as fast as possible to ensure the snare sound had enough high-end frequencies.&lt;/p&gt;
&lt;p&gt;Finally, I settled on XorShift, which was fast and had a basic implementation.&lt;/p&gt;
&lt;h2 id=&quot;data-packing&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#data-packing&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Data Packing&lt;/h2&gt;
&lt;p&gt;After figuring out how to synthesize the song, I needed to figure out how to store it. The trial version of CrossWorks Studio that I was using restricted me to a 16kB code size. I initially wanted to include multiple long songs (although I later scrapped this due to time constraints), so I needed to find an efficient way to store each note, drum, and indicator on the screen.&lt;/p&gt;
&lt;p&gt;I decided early on to try to fit the information for each beat into a small integer and store these integers in an array. I looked into what information I would need to store:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Note pitch (7 bits when stored as MIDI note number)&lt;/li&gt;
&lt;li&gt;Drum sound (2 bits - kick, drum, or neither)&lt;/li&gt;
&lt;li&gt;Indicator (1 bit)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To store each note pitch, I decided to use MIDI note numbers. These only use 7 bits per note, and they can be converted to frequencies using a basic formula, so this was a much better solution than trying to store the note frequency or wavelength.&lt;/p&gt;
&lt;p&gt;10 bits is kind of an odd size, so I tried to figure out what else I could include to use all bits in a 16 bit integer. The first thing I added was duty cycle controls. The original NES had 3 duty cycle settings, and composers could create interesting &lt;a href=&quot;https://www.youtube.com/watch?v=kl9v8gtYRZ4&quot;&gt;effects&lt;/a&gt; by switching between them. I decided to add 4 duty cycle settings to this project, although they didn’t sound as different as I had hoped (likely due to the poor quality speaker I used). This brought the total up to 12 bits.&lt;/p&gt;
&lt;p&gt;Finally, I came up with the idea of including a “message length” field which would specify how many beats after this one were to be held out. This could drastically compress the resulting array by removing duplicate entries. I made this 4 bits long, allowing for up to 16-beat messages.&lt;/p&gt;
&lt;p&gt;Here’s the spreadsheet I built to pack these messages together for me. On the left, you can set the parameters of each note: is a marker shown on the display? what specific note is played? for how long? is there a drum sound? etc. On the right is each section packed into a single 16-bit integer. An array of these integers can be included with the game code to play back the song. By switching the tab at the bottom, you can see both of the songs I included with the game.&lt;/p&gt;
&lt;iframe class=&quot;w-full h-96&quot; src=&quot;https://docs.google.com/spreadsheets/d/e/2PACX-1vQ2jTL6TOiYkK7ZLyM8OinKNpnfOwafpIabo_0DhFtii-M3KLkS-VDod56g5RjTcI22kW2fR8Yx7kno/pubhtml?widget=true&amp;#x26;headers=false&quot;&gt;&lt;/iframe&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;I had fun working on this, and I learned a lot about programming for embedded systems. The results didn’t sound spectacular, but they still enhanced the final game in my opinion.&lt;/p&gt;</content:encoded></item><item><title>Breqbot</title><link>https://breq.dev/projects/breqbot/</link><guid isPermaLink="true">https://breq.dev/projects/breqbot/</guid><description>A Discord bot with minigames and utilities.</description><pubDate>Thu, 08 Oct 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;317&quot; height=&quot;120&quot; src=&quot;https://breq.dev/_astro/8ball.BaQ31vr-_1Y68sm.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;One of the many &quot;just for fun&quot; commands that Breqbot has.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A previous version of this post had a link to invite the bot to your server. I have removed this link for a few reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The bot was built for a small set of friends half a decade ago when myself and my friend group both were very different from how they are now&lt;/li&gt;
&lt;li&gt;I have no interest or motivation in keeping this bot running&lt;/li&gt;
&lt;li&gt;While I remain proud of the work I put into this bot, it provides no particular utility compared to other bots out there&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The GitHub repo will remain accessible.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;Breqbot is a Discord bot that manages a virtual economy, provides several fun minigames, gives access to comics, and has a variety of other features. Users can add Breqbot to a Discord server with their friends, giving their community access to these features.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;A couple things came together to make this project happen. First, one of my friends invited me to a Discord server where they were using a variety of other popular Discord bots. I wasn’t a fan of how some of these bots ran their economy or other features, and I wanted to see if I could implement something better. Second, my work on &lt;a href=&quot;https://breq.dev/projects/mcstatus/&quot;&gt;McStatus&lt;/a&gt;’s backend using Heroku gave me experience working with microservice architectures, and I wanted to work on a more complex microservices project.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;A running Breqbot instance consists of three containerized processes: the web process, the Discord worker process, and the &lt;a href=&quot;https://redis.io/&quot;&gt;Redis&lt;/a&gt; instance.&lt;/p&gt;
&lt;h2 id=&quot;the-discord-worker&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-discord-worker&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Discord Worker&lt;/h2&gt;
&lt;p&gt;The discord worker is written using &lt;a href=&quot;https://github.com/Rapptz/discord.py/&quot;&gt;discord.py&lt;/a&gt; using its &lt;a href=&quot;https://discordpy.readthedocs.io/en/latest/ext/commands/index.html&quot;&gt;bot commands framework&lt;/a&gt;. This process listens for Discord events over the Discord Gateway, selects an appropriate command, and executes it. Here are some examples of these commands:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;303&quot; height=&quot;318&quot; src=&quot;https://breq.dev/_astro/2048.Cc_xJsDB_1SbgU3.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;By adding reactions to the message (the arrow emojis), members of the Discord server can work together to play the famous browser game “2048.” Breqbot will listen for these reactions, modify the internal game state, and update the game board displayed.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;479&quot; height=&quot;313&quot; src=&quot;https://breq.dev/_astro/profile.CW2EkcLa_Z1BxXTd.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Breqbot allows a user to configure their profile, which can then be displayed on request by other users. These profiles are images drawn using &lt;a href=&quot;https://pillow.readthedocs.io/en/stable/&quot;&gt;Pillow&lt;/a&gt;, a Python library for image manipulation.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;565&quot; height=&quot;257&quot; src=&quot;https://breq.dev/_astro/vex.CdTdNiKQ_15E8Th.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Breqbot can pull Vex Robotics Competition data from &lt;a href=&quot;https://vexdb.io/&quot;&gt;VexDB&lt;/a&gt; and create a summary of a team’s performance. Here’s my team from 2019-2020.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;593&quot; height=&quot;332&quot; src=&quot;https://breq.dev/_astro/xkcd.CunLvK3w_sj6Dk.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Breqbot can share comics from a few series including the famous &lt;a href=&quot;https://xkcd.com/&quot;&gt;xkcd&lt;/a&gt;. Additionally, a worker task will continuously monitor these comics for updates, and it can be configured to automatically post them to certain channels.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;590&quot; height=&quot;169&quot; src=&quot;https://breq.dev/_astro/soundboard.CsOW3HmN_njIMG.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The Soundboard app lets server members add sounds (in the form of YouTube links) and corresponding emoji. Then, if someone reacts to the soundboard using that emoji, the corresponding sound will play in the server’s voice channel.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;384&quot; height=&quot;210&quot; src=&quot;https://breq.dev/_astro/roles.CMcEP4aM_ZVGxOK.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Discord has a powerful “Roles” feature to help identify members of a server, but it does not have a way for users to assign themselves roles. Breqbot provides a “reaction roles” menu system - by reacting to this message, a user can select which roles they would like to receive, and Breqbot will automatically modify their roles as necessary.&lt;/p&gt;
&lt;h2 id=&quot;the-redis-instance&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-redis-instance&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Redis Instance&lt;/h2&gt;
&lt;p&gt;Most of Breqbot’s features rely on &lt;a href=&quot;https://redis.io/&quot;&gt;Redis&lt;/a&gt; to store user data. For example, the role menu will store the set of messages it needs to watch reactions for, and the profile feature will store a user’s configuration in Redis. This allows the Discord worker to be restarted at any time with minimal disruption.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;449&quot; height=&quot;424&quot; src=&quot;https://breq.dev/_astro/reddit.Bo_tbGvT_h6Eah.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;One notable use of Redis is to cache Reddit posts. Breqbot uses &lt;a href=&quot;https://praw.readthedocs.io/en/latest/&quot;&gt;PRAW&lt;/a&gt; to automatically retrieve popular posts from Reddit to display. However, the Reddit API is slow, and it does not provide a method to choose a random popular post. Because of this, Breqbot uses a background task to periodically retrieve the 100 most popular posts from a variety of subreddits and store them in Redis. Then, when a user requests a post from one of these subreddits, Breqbot can retrieve it from its cache.&lt;/p&gt;
&lt;h2 id=&quot;the-web-process&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#the-web-process&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;The Web Process&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;948&quot; height=&quot;942&quot; src=&quot;https://breq.dev/_astro/website.CgII_o06_ZJsjkQ.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The web process runs Breqbot’s accompanying website. If a Discord server chooses to enable it, Breqbot can publish information about that server’s economy and its members to a website URL. The web process will use Redis to get this information - it does not communicate directly with the Discord worker.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;612&quot; height=&quot;419&quot; src=&quot;https://breq.dev/_astro/portal.B8pLSPvr_Z1Q1smS.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The Portal API allows other programs to connect to a Breqbot instance to provide additional functionality. In this example, the Portal (which was hosted on my laptop) will echo back any input it receives. A more practical use of this would be to build functionality that interacts with the real world, such as a remote-control robot that communicates over Discord.&lt;/p&gt;
&lt;p&gt;Portal clients connect to Breqbot using WebSockets, which are handled by the web process. Requests and responses are sent between the Discord and Web processes through a Redis pub/sub channel. Management of Portal clients, including distribution of API keys, is handled by the Discord worker: if a user wants to register a new portal, they will receive their API keys through a direct message on Discord.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;This was my first project that ended up being used by such a wide audience: many of my friends added it to their communities on Discord, and people were constantly requesting functionality or finding bugs. I really enjoyed the experience of developing this and using it with my friends, though, and it was rewarding to see other people enjoying my work.&lt;/p&gt;</content:encoded></item><item><title>LPS System</title><link>https://breq.dev/projects/lps/</link><guid isPermaLink="true">https://breq.dev/projects/lps/</guid><description>A simple, calibration-free, vision-based local positioning system.</description><pubDate>Tue, 01 Sep 2020 00:00:00 GMT</pubDate><content:encoded>&lt;iframe src=&quot;https://www.youtube.com/embed/4wxjDMXGJJ0?si=s_ofJ9itNmFts5d6&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; class=&quot;w-full aspect-video rounded-2xl overflow-clip&quot;&gt;&lt;/iframe&gt;
&lt;p class=&quot;caption&quot;&gt;A quick demonstration of the system in action.&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;The LPS (Local Positioning System) is a system to help robots determine their location in a predefined area. This system uses a camera and colored markers to determine the position of robots and other objects in a scene, then relays that information to robots over a network connection.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;Localization in robotics is a difficult but important problem. Through the Vex Robotics Competition, I’ve had experience trying to get a robot to perform pre-programmed movements. I’ve also worked on several automated robots outside of VRC. In all cases, I’ve been dissatisfied with the poor options available for determining a robot’s position.&lt;/p&gt;
&lt;h2 id=&quot;current-systems&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#current-systems&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Current Systems&lt;/h2&gt;
&lt;h3 id=&quot;accelerometer&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#accelerometer&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Accelerometer&lt;/h3&gt;
&lt;p&gt;Because an accelerometer measures acceleration, it must be double-integrated to track the robot’s position. This means that any error will drastically affect the result over time, and the calculated position will rapidly lose precision.&lt;/p&gt;
&lt;h3 id=&quot;encoder-wheels&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#encoder-wheels&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Encoder Wheels&lt;/h3&gt;
&lt;p&gt;By placing encoders on wheels, a robot can track its movement over time. VRC team 5225A, “πlons,” used a technique with three separate encoders to track the robot’s movement, its slip sideways, and its orientation (described here). However, any error (wheel slop, robot getting bumped or tipping, etc) will accumulate over time with this method. πlons needed to supplement their tracking system by occasionally driving into the corners of the VRC field in order to reset the accumulated error.&lt;/p&gt;
&lt;h3 id=&quot;global-positioning-system&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#global-positioning-system&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Global Positioning System&lt;/h3&gt;
&lt;p&gt;GPS can work well for tracking a robot’s general position in a large area. However, common receivers have an accuracy of only about 3 meters, and while modules with centimeter-level accuracy are available, they are prohibitively expensive.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1920&quot; height=&quot;1080&quot; src=&quot;https://breq.dev/_astro/pattern_wall.B_t4S4Ha_Z1aGViF.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;The &quot;Vex GPS&quot;: An example of the &quot;Pattern Wall&quot; approach to localization.&lt;/p&gt;
&lt;h3 id=&quot;pattern-wall&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#pattern-wall&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Pattern Wall&lt;/h3&gt;
&lt;p&gt;By placing a specific code along the walls of the area, a camera mounted on a robot can determine its position and orientation. This method will be used in the Vex AI Competition. Other objects in the field can block the robot’s view of the pattern wall. Additionally, this system requires a defined playing field with straight sides and walls, and each robot is required to have its own camera and computer vision processing onboard.&lt;/p&gt;
&lt;h3 id=&quot;lidar&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#lidar&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;LiDAR&lt;/h3&gt;
&lt;p&gt;A LiDAR sensor can create a point cloud of a robot’s surroundings, and SLAM (Simultaneous Localization And Mapping) algorithms can automatically map an area and determine the robot’s position relative to other objects. However, LiDAR sensors are large and expensive, they must be mounted with a clear view of the robot’s surroundings, and the robot must have enough processing power to run these algorithms. Additionally, this system will not work as well in an open environment, where there won’t be any objects for the LiDAR to detect, and it might have difficulty determining the difference between stationary objects (such as walls) and moving objects (such as other robots in the area).&lt;/p&gt;
&lt;h3 id=&quot;radio-trilateration&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#radio-trilateration&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Radio trilateration&lt;/h3&gt;
&lt;p&gt;If a robot is able to determine the relative signal strength of multiple different radios (such as WiFi access points or Bluetooth beacons), and it knows the positions of these radios, it can estimate its position relative to them by assuming signal strength correlates with distance. Because there are many other variables that affect signal strength, such as objects between the robot and radio, this method is not very reliable. Also, robots that use wireless communication will need to make sure their transmissions do not affect their ability to determine their location. This problem is worse if multiple robots are present in an area.&lt;/p&gt;
&lt;h2 id=&quot;new-system&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#new-system&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;New System&lt;/h2&gt;
&lt;p&gt;In the past, I have more or less given up on systems which do not accumulate error and instead programmed the robot to navigate using an open-loop system and then drive into a wall (of known position) every so often. As this solution doesn’t work well outside of a clearly-defined environment, I wanted to see if I could come up with a better solution for localization.&lt;/p&gt;
&lt;p&gt;I set the following criteria for such a system:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Work well in scenarios where multiple robots are present&lt;/li&gt;
&lt;li&gt;Not require complex processing on each robot&lt;/li&gt;
&lt;li&gt;Be simple to set up in a given area (not requiring calibration)&lt;/li&gt;
&lt;li&gt;Be relatively inexpensive and use commonly available parts&lt;/li&gt;
&lt;li&gt;Not accumulate error over time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I decided to primarily target a scenario with multiple tracked objects in a single area. Putting the heavy processing outside of the robot allows for more inexpensive robots powered by basic microcontrollers. Additionally, this allows passive objects in the scene to be tracked, allowing robots to interact with them.&lt;/p&gt;
&lt;p&gt;I decided to try implementing a solution that worked outside the plane that the robots would be in, so that it would not be affected by adding more robots or objects into the area. Cameras are common and relatively cheap, so using a camera mounted on a tripod and pointed down at robots seemed like the best approach.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;h2 id=&quot;1-image-scanning&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#1-image-scanning&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;1. Image Scanning&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;722&quot; height=&quot;280&quot; src=&quot;https://breq.dev/_astro/image-scanning-1.C6qeZv6U_Cmt3q.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The system starts with a picture of the scene (left image). I’ve placed a computer vision marker in the scene along with a bunch of random objects.&lt;/p&gt;
&lt;p&gt;Markers have a blue border around them to distinguish them from the environment. I used painters’ tape for this, as it has a consistent hue in a variety of lighting conditions. The system selects all the pixels in the image with this hue (and ample lightness and saturation).&lt;/p&gt;
&lt;p&gt;This mask is shown in the right image. The marker is plainly visible here, and almost all of the other objects in the scene have been filtered out. The pencil and textbook happen to have the same hue, but they will be filtered out later.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;704&quot; height=&quot;272&quot; src=&quot;https://breq.dev/_astro/image-scanning-2.fo_rrXrj_ZLvMHm.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The first step in handling the masked areas is to determine their boundary. OpenCV has a convenient function for tracing the border of each region of interest.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;713&quot; height=&quot;280&quot; src=&quot;https://breq.dev/_astro/image-scanning-3.B_2s_2uq_ZrCxmH.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now, we have a series of points tracing the boundary of the marker. This isn’t a perfect quadrilateral for a variety of reasons (the camera introduces noise to the image, the lens introduces distortion, the marker isn’t completely square, the image resolution is low so the image is a bit blurry, etc). To approximate this boundary to four points, with one at each corner, we can use the Douglas-Peucker algorithm (conveniently also implemented by OpenCV).&lt;/p&gt;
&lt;p&gt;We can also do some filtering here to discard areas that aren’t markers. For instance, areas that are very elongated, aren’t quadrilaterals, or are very small probably aren’t valid markers. This leaves us with the marker in the image, surrounded by a red bounding box.&lt;/p&gt;
&lt;h2 id=&quot;2-transformation-calculation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#2-transformation-calculation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;2. Transformation Calculation&lt;/h2&gt;
&lt;p&gt;The next step is to figure out what type of marker this is, and what its orientation is. Because there might be multiple markers in a scene, the black and white squares on the inside of the blue border give information on what type of marker it is.&lt;/p&gt;
&lt;p&gt;Using the bounding quadrilateral of the marker, we can calculate where we expect the squares to be. To do this, we need to calculate the transformation between the basis of the picture (i.e., with the origin at the top-left pixel, x-axis as the top row of pixels, and y-axis as the left row of pixels) and the basis of the scene. For reading the squares, it’s convenient to set the origin of the scene at the center of the marker, choose x and y axes arbitrarily, and set the length of each square to 1 unit.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;622&quot; height=&quot;470&quot; src=&quot;https://breq.dev/_astro/transformation-calculation-1.DLzjAlKX_1x69fy.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;It’s apparent that the sides of the marker aren’t parallel lines in the image, even though they are parallel in real life. Because the camera is a perspective camera, we need to use a perspective transformation (in which parallelism isn’t preserved). In order to calculate this type of transformation, we need to know the coordinates of four points in each basis. Conveniently, our quadrilateral bounding the marker has four points which we know the image coordinates of, and we know the real-life position of the marker’s corners relative to its center as well.&lt;/p&gt;
&lt;p&gt;In order to compute a perspective transformation, we need a new coordinate system. Consider a point which appears on the horizon: it would be mapped an infinitely far distance away. Representing this isn’t possible with only X and Y coordinates, so we need a third coordinate.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;614&quot; height=&quot;648&quot; src=&quot;https://breq.dev/_astro/transformation-calculation-2.BHXSBp_l_Z17lkv5.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The solution is a system known as homogeneous coordinates. To map our existing cartesian coordinates to homogeneous ones, we just include a 1 as the third coordinate. To map them back to cartesian coordinates, divide the X and Y coordinates by the third coordinate. This solves our horizon-point problem: Points with a third coordinate of 0 will have no defined equivalent cartesian coordinate.&lt;/p&gt;
&lt;p&gt;(While the system is operating normally, points on the horizon line shouldn’t ever actually be visible. Despite this, we still need to consider it in order to define the perspective transformation.)&lt;/p&gt;
&lt;p&gt;Using our new homogenous coordinates, we can calculate a transformation matrix which transforms coordinates in the picture to coordinates in real life (and invert that matrix to go the other way).&lt;/p&gt;
&lt;h2 id=&quot;3-marker-identification&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#3-marker-identification&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;3. Marker Identification&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;640&quot; height=&quot;480&quot; src=&quot;https://breq.dev/_astro/marker-identification-1.VHm-6uEq_Z19pfKB.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Because we know the real-life coordinates of the corners of each square, we can use the transformation matrix to map them to coordinates of pixels in the picture. The yellow grid in the image shows where the squares are expected to be.&lt;/p&gt;
&lt;p&gt;At least one square is guaranteed to be each color. In order to compensate for varying lighting conditions, the system identifies the darkest and lightest squares, and then compares the lightness of the other squares to determine if they are colored black or white. On the left of the image, the grey square is the average lightness of the darkest square on the marker, and the white square is the average lightness of the lightest square on the marker.&lt;/p&gt;
&lt;p&gt;No pattern of squares is rotationally symmetrical, so the orientation of the pattern can be used to determine the orientation of the marker. The system can now draw the position and orientation of the marker over the camera feed.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;640&quot; height=&quot;480&quot; src=&quot;https://breq.dev/_astro/marker-identification-2.DooVeupM_1M3pdk.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;4-global-reference&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#4-global-reference&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;4. Global Reference&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;737&quot; height=&quot;289&quot; src=&quot;https://breq.dev/_astro/global-reference-1.DOHG993__iwR7Q.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I’ve added a few new markers into the scene. These markers have a different code on them: one black square and three white squares. I’ve also placed down a ruler for scale.&lt;/p&gt;
&lt;p&gt;The system will distinguish between the original markers and the new markers. It plots each of the new markers on a grid, as well, relative to the original marker. You can see from the ruler that the furthest-right marker is about 12” away horizontally and 0” away vertically from the original marker, and the system plots its position as (12, 0).&lt;/p&gt;
&lt;p&gt;The different square codes signify the different types of marker present. The original marker, with its 3-black, 1-white code, is the global reference - its position and size are used to plot the positions of the other markers. This means that the size of the global reference matters, but the size of the other markers does not (and you can see each marker in the image is a different size).&lt;/p&gt;
&lt;p&gt;Mathematically, the hard work is already done: we can use the transformation matrices for the reference marker that have already been calculated. In a previous step, the system used the transformation matrix on the corners of each square, mapping each point’s coordinates in the scene to its coordinates in the picture. Similarly, we can use the reference’s inverse transformation matrix to map each additional marker’s coordinates in the picture to its coordinates in the scene.&lt;/p&gt;
&lt;p&gt;The system plots an overhead-view grid of each visible marker. Note that the orientation and position of each marker is visible. Additionally, the size of each marker is known: smaller markers have a shorter arrow.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;500&quot; height=&quot;500&quot; src=&quot;https://breq.dev/_astro/global-reference-2.CmG3bN6j_ZHjqL2.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;5-post-processing-and-api&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#5-post-processing-and-api&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;5. Post-Processing and API&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;500&quot; height=&quot;500&quot; src=&quot;https://breq.dev/_astro/post-processing-1.rXk_KTnl_Z2pTE3x.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The raw plot can be kind of jittery because of image noise, low camera resolution, etc. The system will average the position of each marker over time to get a more stable and accurate plot. This smooth plot is also available using a REST API for other devices to use.&lt;/p&gt;
&lt;p&gt;In order to do this, however, the system needs to keep track of each marker. In each new frame, when markers are found, it needs to determine if each marker is a new marker introduced into the scene or an existing marker being moved. To help keep track of markers across frames, letters are assigned. This also helps API clients keep track of markers.&lt;/p&gt;
&lt;h2 id=&quot;additional-feature-labels&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#additional-feature-labels&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Additional Feature: Labels&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;738&quot; height=&quot;294&quot; src=&quot;https://breq.dev/_astro/labels-1.BcPq-QQs_gtonY.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;There is another type of marker with 2 black squares. These are labels, and they contain additional squares below the marker to identify it. These squares are read the same way as the squares inside the marker. As shown in the readout, this label has the number “1”. Labels show up with their number on both the raw plot and the smooth plot, and are identified by their number through the API.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;664&quot; height=&quot;344&quot; src=&quot;https://breq.dev/_astro/labels-2.BPw88_6m_Z1055MR.webp&quot; srcset=&quot;&quot;&gt; &lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;705&quot; height=&quot;283&quot; src=&quot;https://breq.dev/_astro/labels-3.BoXB125x_26HAnF.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I made a sheet of all possible labels. It’s difficult to see each one on the camera view, but the two plots show each label from 0 to 15.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;681&quot; height=&quot;355&quot; src=&quot;https://breq.dev/_astro/labels-4.UN-PM4Jo_ZQcGmj.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;This project proved that a system using an overhead camera and computer vision markers can be a reliable method of localization for small robots. However, there are definitely issues with the approach that I did not anticipate.&lt;/p&gt;
&lt;h2 id=&quot;unreliable-marker-corner-detection&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#unreliable-marker-corner-detection&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Unreliable Marker Corner Detection&lt;/h2&gt;
&lt;p&gt;In this system, the corners of each marker were used to create the transformation matrices. Because of this, any issues in the detection of these corners will distort the resulting transformation and affect the calculated position of all other objects in the scene. This means that slightly misshapen markers, backgrounds with a similar hue to the markers, and other small issues can cause extremely inaccurate results.&lt;/p&gt;
&lt;p&gt;This could be mitigated by using points within the marker to calculate this transformation, but overall, extrapolating one small reference marker to cover a large area will magnify any errors caused by lens distortion, image blur, etc. and make the entire system much less precise.&lt;/p&gt;
&lt;p&gt;In order to increase the precision of the system, multiple reference markers should be used. However, this raises questions: How will the system know the distance between each marker? Will users be required to precisely set up multiple markers at exact distances from each other? In any case, this would make the system more difficult to deploy and use.&lt;/p&gt;
&lt;h2 id=&quot;unreliable-marker-filtering-from-background-objects&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#unreliable-marker-filtering-from-background-objects&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Unreliable Marker Filtering from Background Objects&lt;/h2&gt;
&lt;p&gt;Because the system detects markers based on color, any other blue objects in the scene may cause markers to be detected which aren’t actually markers. Additionally, color detection is unreliable because changes in lighting type and strength can influence how colors appear.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;726&quot; height=&quot;403&quot; src=&quot;https://breq.dev/_astro/qr_pattern.C6w6o86P_3IqlG.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;
An excerpt from the QR code specification describing how the &quot;Finder Pattern&quot; can be recognized.
&lt;/p&gt;
&lt;p&gt;A common method for detecting other sorts of computer vision markers is to look for some pattern in the lightness of the code. For instance, QR code scanners look for the 1-black, 1-white, 3-black, 1-white, 1-black pattern in the corners of each code. Designing a computer vision marker which can be identified using this technique would significantly reduce the error in this portion.&lt;/p&gt;
&lt;p&gt;However, using a method such as this one would require more processing power, making the system slower and less capable of running on low-end hardware like the Raspberry Pi. It might also reduce the camera resolution that the system can run at.&lt;/p&gt;
&lt;h2 id=&quot;poor-coverage-area&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#poor-coverage-area&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Poor Coverage Area&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;736&quot; height=&quot;628&quot; src=&quot;https://breq.dev/_astro/coverage_area.u5fXxCNa_ZhEfgF.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p class=&quot;caption&quot;&gt;
A diagram of the effective coverage area of my camera-based LPS system.
&lt;/p&gt;
&lt;p&gt;Because of the camera’s limited resolution and field-of-view, the usable area of the system is weirdly shaped. This limits the effectiveness of the system, as most areas are not this weird shape. Multiple cameras could be used to increase the effective area, but this complicates the system and increases the cost significantly.&lt;/p&gt;
&lt;h2 id=&quot;closing-thoughts&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#closing-thoughts&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Closing Thoughts&lt;/h2&gt;
&lt;p&gt;Despite these issues, the system does accomplish the goals I set out at the beginning of the project. However, because of the system’s mediocre accuracy and reliability, I do not anticipate using it for anything in the future in its current state, although I do think this approach to localization is potentially useful (albeit with a lot more refinement).&lt;/p&gt;
&lt;p&gt;I am curious as to how much better this sort of system would work if some of the aforementioned solutions were implemented. A system using four separate reference markers instead of one, QR-style finder pattern codes to help objects be more accurately recognized, and perhaps multiple cameras positioned on opposing sides of the area would likely have a much more useful accuracy. Although such a system would be more difficult and expensive to set up and operate, it would still be a practical and economical solution for providing localization capabilities to one or more robots in an enclosed area.&lt;/p&gt;
&lt;h2 id=&quot;faq-fully-anticipated-questions&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#faq-fully-anticipated-questions&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;FAQ (Fully Anticipated Questions)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What language was this written in?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I built this using Python, with OpenCV handling some of the heavy lifting for computer vision. I chose Python because I was familiar with it, but it would definitely be possible to implement a similar system in JavaScript for a web version.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why invent your own computer vision marker instead of using some existing standard?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I tried to use QR codes, which would’ve saved me from doing most of the computer vision work and would’ve allowed me to focus on the linear algebra side of things, but the camera resolution was too low for that to work well. QR scanner libraries are built to recognize one or two codes positioned close to the camera, not many small codes positioned far away.&lt;/p&gt;
&lt;p&gt;In hindsight, I’m kind of glad I had this problem, because working with computer vision was honestly pretty fun. I hadn’t really done anything in this field before.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why is the camera resolution so low?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I ran this at 480p because OpenCV isn’t able to grab 1080p video from my camera any faster than 5 fps. (Which isn’t really a problem on its own, as things aren’t generally moving that fast, but my camera has rolling shutter, which warped any moving markers on the screen into an unrecognizable state.) The processing done by my software doesn’t really take that much time, so it should be possible to run this sort of system at much higher resolutions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why does the hue filter still capture non-blue things (e.g. the purple pencil and textbook)?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;My camera doesn’t let me turn off auto white balance and automatic exposure compensation, so these parameters were frequently changing while I was trying to work on my system. Because of this, the hue range (and lightness/saturation ranges) I chose had to work for all sorts of white balance and exposure settings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why not just buy or borrow a better camera?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The whole point of this project was to solve a difficult problem - precise localization - using tools I already owned. That means working around the inherent limitations of the hardware. I also wanted to try to make the system as cheap to deploy as possible, so I used my cheap-ish and commonly available webcam.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why blue painters’ tape for the border?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I tried using red, but my skin kept making it past the filter and screwing up the marker detection every time I reached into the scene. I also had a lot of problems with marker on paper being surprisingly glossy: portions of the border were appearing white on the camera instead of blue because of reflected light.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why only 16 possible labels?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;At first, I let labels have an arbitrary number of squares. I found that the further squares were from the marker border, however, the less accurate the calculated position (yellow grid) would be, and these multi-line labels had a lot of scanning errors as a result.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why not use colored squares to determine the marker type? This would let you define many more types of marker.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This works fine for two colors, but trying to distinguish between any more becomes difficult, especially in a variety of lighting and white-balance scenarios. I tried using red-green-blue, but there were too many errors for it to work well.&lt;/p&gt;</content:encoded></item><item><title>McStatus.js</title><link>https://breq.dev/projects/mcstatus/</link><guid isPermaLink="true">https://breq.dev/projects/mcstatus/</guid><description>A JavaScript library to embed information about a Minecraft server into a website.</description><pubDate>Sun, 16 Aug 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;McStatus.js is a JavaScript library and API backend to embed a Minecraft server status readout.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I wanted to be able to check who was online on my Minecraft server without having to join. I also wanted a project where I could improve my understanding of JavaScript, the DOM, and web technologies in general.&lt;/p&gt;
&lt;h1 id=&quot;technical-details&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-details&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Details&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;mcstatus.js&lt;/code&gt; loads a div complete with Minecraft server information into the DOM wherever it finds a &lt;code&gt;&amp;#x3C;div class=&quot;mc-status&quot;&gt;&lt;/code&gt;, using the &lt;code&gt;data-mc-server&lt;/code&gt; attr to set the server IP. The status protocol for Minecraft servers uses raw TCP sockets, so a pure-JS server query-er isn’t possible. There are a lot of existing Minecraft server status tools, like &lt;a href=&quot;https://api.mcsrvstat.us/&quot;&gt;mcsrvstat.us&lt;/a&gt;, but they don’t have a CORS header set, so they couldn’t be used from JavaScript. So, I implemented my own at &lt;code&gt;https://mcstatus.breq.dev/&lt;/code&gt; with the bare minimum API for this project to work. I’m using Dinnerbone’s &lt;a href=&quot;https://github.com/Dinnerbone/mcstatus&quot;&gt;Server Pinger&lt;/a&gt; under the hood for this.&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;It pretty much does everything I wanted it to do, and working on the project definitely gave me a better understanding of how to style websites using CSS. I didn’t really end up using the CSS/JS part for anything, but I did use the server component for &lt;a href=&quot;https://breq.dev/projects/breqbot&quot;&gt;Breqbot&lt;/a&gt;’s Minecraft server functionality.&lt;/p&gt;</content:encoded></item><item><title>Vibrance</title><link>https://breq.dev/projects/vibrance/</link><guid isPermaLink="true">https://breq.dev/projects/vibrance/</guid><description>Vibrance synchronizes computers and smartphones to display configurable animations and effects.</description><pubDate>Mon, 15 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;iframe src=&quot;https://www.youtube.com/embed/uvB-t6f3MoE?si=s_ofJ9itNmFts5d6&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; class=&quot;w-full aspect-video rounded-2xl overflow-clip&quot;&gt;&lt;/iframe&gt;
&lt;p class=&quot;caption&quot;&gt;
Music in the video is made by my sibling, &lt;a href=&quot;https://www.maxmichaelmusic.com/&quot;&gt;Max Michael&lt;/a&gt;.
&lt;/p&gt;
&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;Vibrance is a tool to use a large number of computers or smartphones as a single output to create lighting effects. By setting the color on each screen in unison, colorful and interesting visual effects can be created. Vibrance also handles generating these effects based on some input, such as a Digital Audio Workstation, keyboard input, or custom-made lighting controller.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I remember seeing a video of a Coldplay concert in which every audience member had an LED wristband which would light up in sync with the music. It seemed like a cool way to involve the audience in a concert.&lt;/p&gt;
&lt;p&gt;I wanted to make a light show like the Coldplay concert that would cost almost nothing. No big screen, no manufacturing hundreds of bracelets, just everyone’s personal devices and some controller software.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;The Vibrance system has three main parts: the &lt;em&gt;controller&lt;/em&gt;, the &lt;em&gt;relay&lt;/em&gt;, and the &lt;em&gt;clients&lt;/em&gt;. Working backwards from the clients:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;521&quot; height=&quot;281&quot; src=&quot;https://breq.dev/_astro/vibrance_simple.CDJi8kQL_1h8RBP.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;clients&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#clients&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Clients&lt;/h2&gt;
&lt;p&gt;The client code is a JavaScript app that runs on audience members’ phones or computers. It receives messages from the relay and changes the color displayed on the user’s screen accordingly. These messages are JSON objects, which allows for a variety of extensions to be added. For instance, it is possible to direct a client to display certain text on its screen (song lyrics, a welcome message, etc). Because this code runs in JavaScript, and the messages require very low latency, I chose the WebSocket protocol.&lt;/p&gt;
&lt;h2 id=&quot;relay&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#relay&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Relay&lt;/h2&gt;
&lt;p&gt;Most of the time, both the client devices and the controller will be connected to a public WiFi network or a cell network, and thus be hidden behind NAT. So, an intermediate server with a public IP address is required to facilitate the connection. The Relay fills this role.&lt;/p&gt;
&lt;p&gt;Because it would be impractical for the controller to calculate separate colors for hundreds of clients, the clients are divided into zones. Client devices will indicate their zone to the relay (typically after prompting the user to choose which part of the room they are in). Messages sent to the relay by the controller are marked with their destination zone.&lt;/p&gt;
&lt;p&gt;Upon receiving a message from the controller, the relay forwards it along to each device in its intended zone. In the background, the relay manages newly-connected clients, ensures connections are kept alive, and removes inactive clients.&lt;/p&gt;
&lt;h2 id=&quot;controller&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#controller&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Controller&lt;/h2&gt;
&lt;p&gt;The controller is the most complicated part of the pipeline. It is responsible for handling input from some device (through a &lt;em&gt;driver&lt;/em&gt;), determining the color messages to send to the relay (using an &lt;em&gt;interface&lt;/em&gt; and &lt;em&gt;script&lt;/em&gt;), and sending these messages to the relay.&lt;/p&gt;
&lt;h3 id=&quot;driver&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#driver&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Driver&lt;/h3&gt;
&lt;p&gt;The role of the Driver is to read input from some source and return it in a common format. Drivers can use a variety of inputs, such as MIDI ports, computer keyboards, and serial ports (e.g. for use with Arduino).&lt;/p&gt;
&lt;p&gt;Virtual MIDI ports in particular are quite useful for reading input from a Digital Audio Workstation such as Ableton Live. By placing MIDI notes at certain points in the song, lighting effects can automatically be triggered.&lt;/p&gt;
&lt;p&gt;Users can implement their own Drivers to use different controller devices (joysticks, Wii remotes, devices over the network, etc.)&lt;/p&gt;
&lt;h3 id=&quot;interface-and-scripts&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#interface-and-scripts&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Interface and Scripts&lt;/h3&gt;
&lt;p&gt;Users supply Scripts (written in Python) that describe how to translate inputs from Drivers into messages to send to the Relay. These scripts use an Interface to manage the drivers and relay connection. The Interface allows Vibrance to recover gracefully from a temporary connection failure, and it allows users to change the connected Driver and Relay at any time.&lt;/p&gt;
&lt;p&gt;For example, here is an example script that flashes zones 1 and 2, with a 1 second delay in between, whenever MIDI note 60 is received:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;midi&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;note_on_60&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;FFF&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;000&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;wait&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;FFF&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;color&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;000&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A variety of example scripts are provided by Vibrance.&lt;/p&gt;
&lt;h2 id=&quot;block-diagram&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#block-diagram&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Block Diagram&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;891&quot; height=&quot;801&quot; src=&quot;https://breq.dev/_astro/vibrance.Bp-37uSW_Z2skIQN.svg&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;I don’t think I’ll have an opportunity to test Vibrance in the real world any time soon, because of the COVID-19 pandemic. However, I have tested it with multiple devices and with hundreds of simulated clients at my house.&lt;/p&gt;
&lt;p&gt;I started this project a few weeks (!) before the stay-at-home orders started, and I had initially planned on trying it at the next opportunity my sibling had to perform their music. As an alternative, in the video I filmed demonstrating Vibrance, I emulated their Ableton Live setup as closely as possible.&lt;/p&gt;</content:encoded></item><item><title>Red Storm Robotics</title><link>https://breq.dev/projects/344/</link><guid isPermaLink="true">https://breq.dev/projects/344/</guid><description>I helped start one of VRC Maine&apos;s youngest and strongest teams.</description><pubDate>Sat, 13 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;iframe src=&quot;https://www.youtube.com/embed/EVbv0NNOtKE?si=s_ofJ9itNmFts5d6&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; class=&quot;w-full aspect-video rounded-2xl overflow-clip&quot;&gt;&lt;/iframe&gt;
&lt;p class=&quot;caption&quot;&gt;Here&apos;s a video of teams 4393X and 4393Z vs. 344R and 344X. At the time, I was on team 344R. 4393X was made up of the people I worked with back at MSSM.&lt;/p&gt;
&lt;p&gt;When I returned to Scarborough, I worked with the engineering teacher to start a VRC team. I helped with searching for and applying for grants, ordering parts, and building out a workspace. I then took on a leadership role in Team 344X where I helped show my teammates how to design solutions with VEX parts, how to build efficiently, how to program the robots, and (above all) how to think critically about a challenge.&lt;/p&gt;
&lt;p&gt;In January, the team raised enough money from grants to spin off a second team, 344R. Several other students and I built a second robot (effectively starting halfway through the competition season) and competed under that number, eventually qualifying for the state championship and the world championship (both of which were canceled due to COVID-19).&lt;/p&gt;
&lt;p&gt;In summer 2020, I decided to give back to the VRC community by contributing code to OkapiLib, a library used by hundreds of VEX teams for advanced functions such as motion profiling, PID control, and asynchronous movement. Notably, the 2019 World Skills Champions used OkapiLib for path generation in their Programming Skills routine. The library has almost ten thousand downloads on GitHub.&lt;/p&gt;
&lt;p&gt;My &lt;a href=&quot;https://github.com/OkapiLib/OkapiLib/pull/445&quot;&gt;pull request&lt;/a&gt; added support for trigonometry, rounding, exponentiation, and other math functions to OkapiLib’s units API. Code that uses this API is checked at compile-time for common units errors, eliminating most mistakes caused when a programmer enters a formula incorrectly. My addition of math functions allows users to write formulas that use more than the basic four functions (add, subtract, multiply, divide) without sacrificing this compile-time units checking.&lt;/p&gt;</content:encoded></item><item><title>MSSM Penguins Robotics</title><link>https://breq.dev/projects/4393/</link><guid isPermaLink="true">https://breq.dev/projects/4393/</guid><description>Our rookie team competed and won a World Championship slot.</description><pubDate>Sat, 13 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;iframe src=&quot;https://www.youtube.com/embed/nEkfTPRDO8k?si=s_ofJ9itNmFts5d6&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; class=&quot;w-full aspect-video rounded-2xl overflow-clip&quot;&gt;&lt;/iframe&gt;
&lt;p class=&quot;caption&quot;&gt;
Here&apos;s the autonomous routine I planned, developed, and tuned for the 2019 Maine State Championship on team 4393S. This autonomous routine got us the highest autonomous points ranking out of all 50 teams at the event.
&lt;/p&gt;
&lt;p&gt;This was my first experience with a robotics competition. I did most of the building and all of the programming for my team. Our team won two local tournaments, was a finalist at the state championship, won the design award at the state championship, and competed at the world championship. This experience improved my mechanical skills, understanding of the design process, embedded programming skills, and teamwork abilities.&lt;/p&gt;
&lt;p&gt;Here are a few of the things I’m most proud of from my work on this team:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automatic aim using the VEX Vision Sensor to recognize flags&lt;/li&gt;
&lt;li&gt;Buttons to automatically shoot multiple flags in quick succession using state machine logic, including sensors to track the position of balls, automatic reloading logic, and angle presets&lt;/li&gt;
&lt;li&gt;Movement in an arc for saving time during the autonomous routine&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Mindjacker</title><link>https://breq.dev/projects/mindjacker/</link><guid isPermaLink="true">https://breq.dev/projects/mindjacker/</guid><description>A Python library wrapper to interact with the NXT</description><pubDate>Sat, 13 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;overview&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#overview&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Overview&lt;/h1&gt;
&lt;p&gt;This was a wrapper for &lt;a href=&quot;https://github.com/Eelviny/nxt-python&quot;&gt;nxt-python&lt;/a&gt; I wrote while I was in middle school for projects like the &lt;a href=&quot;https://breq.dev/projects/r2d2&quot;&gt;R2D2&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;motivation&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#motivation&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Motivation&lt;/h1&gt;
&lt;p&gt;I liked the featureset of nxt-python, but I wanted to make it more Pythonic and add some common features (like playing audio) that I often used in robots.&lt;/p&gt;
&lt;h1 id=&quot;technical-description&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#technical-description&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Technical Description&lt;/h1&gt;
&lt;p&gt;It’s a wrapper API, there’s not much to describe.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; data-language=&quot;python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; mindjacker

brick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mindjacker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Brick&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

brick&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;move&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;b&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;c&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rotations&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; steer&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; brake&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
brick&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;playSound&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;sound.mp3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

measurement &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; brick&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ultrasonic&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Ultrasonic sensor measures &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;measurement&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; inches&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

brick&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;write&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data.log&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; measurement&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;results&quot; class=&quot;group&quot;&gt;&lt;a class=&quot;relative&quot; href=&quot;#results&quot;&gt;&lt;span class=&quot;anchor&quot;&gt;#&lt;/span&gt;&lt;/a&gt;Results&lt;/h1&gt;
&lt;p&gt;This was one of the first APIs I actually designed. It’s a pretty flawed design, but it was a learning experience. This was also one of the first times I wrote software to make it easier for me to write more software, and I decided to make it open-source.&lt;/p&gt;</content:encoded></item></channel></rss>