<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Matt 🦕</title>
    <description>The latest articles on DEV Community by Matt 🦕 (@mattjbones).</description>
    <link>https://dev.to/mattjbones</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F149720%2F503b043b-4f03-4a9f-bee9-d47ae439ea3f.jpg</url>
      <title>DEV Community: Matt 🦕</title>
      <link>https://dev.to/mattjbones</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mattjbones"/>
    <language>en</language>
    <item>
      <title>ffconf22</title>
      <dc:creator>Matt 🦕</dc:creator>
      <pubDate>Tue, 29 Nov 2022 12:37:47 +0000</pubDate>
      <link>https://dev.to/mattjbones/ffconf22-408g</link>
      <guid>https://dev.to/mattjbones/ffconf22-408g</guid>
      <description>&lt;h2&gt;
  
  
  tl;dr
&lt;/h2&gt;

&lt;p&gt;ffconf22 allowed me to reconnect with what I love about frontend. The day also doubled as a great introduction to a community of folks who are just as passionate about frontend as me. It was worth the early start. I'm already looking forward to next year.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;6am&lt;/strong&gt; &lt;em&gt;mmm let's go&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;This was me heading down to Brighton for ffconf, having been encouraged to sign up during the summer. Navigating a tube strike. Sourcing something resembling breakfast.&lt;/p&gt;

&lt;p&gt;Work was chaotic given the recent ownership change, so I was too busy to plan ahead or overthink the trip down to ffconf. But getting out of London and taking the time away from work was just what I needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;9am&lt;/strong&gt; &lt;em&gt;Duke of York's, cinema and coffee&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Conferences are always a little nervy in the beginning. There's a brief moment before it starts where you're kinda fending for yourself. I had come down with a friend, but they had made themselves scarce at the key moment of mingling. &lt;/p&gt;

&lt;p&gt;This conflict between shyness and socialising was easily overcome by a strong urge to find coffee. In I went. A quick stop at the table to pick up stickers, badges and a notebook - I love a good notebook during a conf. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2F%2Fpublic%2F2022-11-13%2Fduke-of-yorks.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2F%2Fpublic%2F2022-11-13%2Fduke-of-yorks.jpeg" title="The excellent Duke of York's baring their stripy tights proudly" alt="Duke of York's" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upstairs I found the coffee and a cute little balcony overlooking the groups of ffconf'ers below. Old friends? Colleagues? First timers? I would find out later. For now, I got chatting with &lt;a href="https://mobile.twitter.com/ciaran128" rel="noopener noreferrer"&gt;Ciaran&lt;/a&gt; who happened to have a spot next to him on the balcony. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;9:40&lt;/strong&gt; &lt;em&gt;hello Remy&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;We ended up sitting in the front row with a couple of friends behind. Excellent view of the stage and speakers. The Duke of York's is usually a cinema and happens to have the world's comfiest seats - dangerous for those early starters who were missing out on sleep, but we had nowhere to hide.&lt;/p&gt;

&lt;p&gt;I had a quick look over the schedule on the lanyard and saw &lt;a href="https://mobile.twitter.com/type__error" rel="noopener noreferrer"&gt;Sophie&lt;/a&gt; was talking - looking forward to that, everything else would be a nice surprise. It did not disappoint. &lt;/p&gt;

&lt;p&gt;Remy, one of the organisers, filled the room full of energy the moment he stepped on stage. After the usual preamble, we were on to our first talk. Cue the first of many talks all the while hurriedly note-taking and listening intently. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;09:50&lt;/strong&gt; &lt;em&gt;Florence and Heydon&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Florence Okoye (&lt;a href="https://mobile.twitter.com/FINOkoye" rel="noopener noreferrer"&gt;@FINOkoye&lt;/a&gt;) kicked off the conference with &lt;em&gt;"Designing as we want, to create the experiences that we need"&lt;/em&gt;. Straight up a super informative talk which touched on several elements of design theory. I'll need to make time to give &lt;a href="http://www.moderntimesworkplace.com/archives/ericsess/sessvol2/19CHERNS.pdf" rel="noopener noreferrer"&gt;Socio-Technical Design&lt;/a&gt; a read at some point.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2022-11-13%2Fsocio-technical-design.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2022-11-13%2Fsocio-technical-design.jpeg" title="The stage is set for Florence to dive into Socio-Technical design, the slides are an animating gradient between purple and and yellow and the various elements of the theory are written on top." alt="Socio-Technical Design" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As someone who doesn't have an academic background in this space, I found comfort in hearing about tools/techniques that I had seen in practice e.g. "How Might We" and bringing the vital sense of community to the forefront of decision-making. Similarly, I think it would be worth remembering this definition of a designer; "&lt;em&gt;[someone]&lt;/em&gt; who moves things from an existing condition to a preferred one".&lt;/p&gt;

&lt;p&gt;I had not heard of the &lt;a href="https://designjustice.org/read-the-principles" rel="noopener noreferrer"&gt;Design Justice&lt;/a&gt; framework, yet had experienced people-centric design at scale. This talk left me yearning for the company we used to be, an aside for another time. Ensuring the community you are building for is right at the centre of your design thinking is a no-brainer, but sometimes things aren't working so ask "are the relationships with the community right?".&lt;/p&gt;

&lt;p&gt;The next speaker was Heydon Pickering (&lt;a href="https://mobile.twitter.com/heydonworks" rel="noopener noreferrer"&gt;@heydonworks&lt;/a&gt;) with &lt;em&gt;"Capitalism, The Web and You"&lt;/em&gt;. Heydon lit up the stage with a fierce dismantling of capitalism and the general idea of "growing the pie" (as it relates to the forever growth that capitalism requires). There were plenty of shock moments in the talk, not least because after each bombshell we were treated to an updated CSS feature that had full modern browser support. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2022-11-13%2Fadvertising.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2022-11-13%2Fadvertising.jpeg" title="The intersection of " alt="The intersection of working life" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The talk had a few standout moments; when he talked about the "privatisation of social capital" it really hit home. Similarly, "obfuscation of theft" is another way that it really strikes a chord. Without going too deep into the talk; given have a finite amount of resources, or capital means that infinite growth is impossible therefore there's a redistribution, rather than growth. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There is no burnout, without capitalism. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Capitalism is, at least I always thought, a necessary evil. However, this talk gave me a fresh perspective. The near faith-like, unquestionable position, that's so far gone now schools aren't able to teach critical thinking around capitalism as they're not allowed to use material from anti-capitalist writers. &lt;/p&gt;

&lt;p&gt;How does the generation ahead have a chance if they're not taught to question?&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;12:00&lt;/strong&gt; &lt;em&gt;Lex and Sophie&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Following a short break, we were back on with Lex Lofthouse (&lt;a href="https://mobile.twitter.com/loftio" rel="noopener noreferrer"&gt;@loftio&lt;/a&gt;) who took us through their talk &lt;em&gt;"Design for Developers"&lt;/em&gt;. This talk had a nice familiarity with the first talk of the day where the former was more theory this was more hands-on. Lex showed some relatively simple, but effective, principles that can be used to improve the overall design.&lt;/p&gt;

&lt;p&gt;The breakdown of "hierarchy", "proximity", "contrast", and "balance" with each having an illustrative slide helped show what to look for. Lex also did a series of before-and-after examples where an "ok" design was jazzed up using the principles. I fit squarely into the category of a pixel-perfect kind of frontend eng and this is exactly the kind of chat I'm here for. &lt;/p&gt;

&lt;p&gt;Alongside the examples were several useful links too e.g. &lt;a href="https://fontjoy.com" rel="noopener noreferrer"&gt;fontjoy.com&lt;/a&gt;, &lt;a href="https://type-scale.com" rel="noopener noreferrer"&gt;type-scale.com&lt;/a&gt;, &lt;a href="https://colour.adobe.com" rel="noopener noreferrer"&gt;colour.adobe.com&lt;/a&gt;, &lt;a href="https://coolers.co" rel="noopener noreferrer"&gt;coolers.co&lt;/a&gt; and &lt;a href="https://fontawesome.com" rel="noopener noreferrer"&gt;fontawseome.com&lt;/a&gt; to name a few.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2022-11-13%2Fcooolers.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2022-11-13%2Fcooolers.jpeg" title="One of the many excellent examples of helpful websites for great desing, coolers - the slide shows the colour theme that was generated, clicking the button again will generate another theme." alt="Coolers palette" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One nice trick that I really liked was the styling of images with different filters/crops etc. This can bring consistency to a bunch of different images and you can see this done to great effect with the ffconf speaker avis. &lt;/p&gt;

&lt;p&gt;Next up was Sophie Koonin (&lt;a href="https://mobile.twitter.com/type__error" rel="noopener noreferrer"&gt;@type__error&lt;/a&gt;) and her excellent &lt;em&gt;"This Talk is Under Construction: a love letter to the personal website"&lt;/em&gt;. She took us through the early days of the wild wild west that was the internet like NeoPets, free ISP hosting options and even the exciting days of Myspace customisation. &lt;/p&gt;

&lt;p&gt;I left the talk remembering the simpler days of the internet, when I built many websites in notepad and hadn't the foggiest what vim was, etc. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;I couldn't leave my own little landing page up without giving it a once over. Check it out at &lt;a href="https://mbarenttjones.com" rel="noopener noreferrer"&gt;mbarnettjones.com&lt;/a&gt;. I guess I also dusted off my jekyll-powered static site generator and wrote this too so double props to Sophie.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2022-11-13%2Fwebsite.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2022-11-13%2Fwebsite.png" title="After Sophie's talk I revisited my own website matt.barnettjones.com and fixed a few rough edges. Atm, it's pretty simple with a photo of a sunset I took when I was in Italy and some links on top. Watch this space." alt="Fresh coat of paint" width="800" height="593"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;12:45&lt;/strong&gt; &lt;em&gt;lunch&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Lunch with future friends was a nice touch where those who were keen could meet other like-minded minglers in some pre-booked lunch spots. A lot of thought has clearly gone into the organisation. &lt;/p&gt;

&lt;p&gt;However, I was away to make new frontend friends with the little crew that had formed during the first part of the day. We headed off to find MEATliquor and tucked in. Dangerously full and ready for a nap we headed back for a quick coffee and the next talks.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;14:00&lt;/strong&gt; &lt;em&gt;Lily and Natalia&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Programming a computer can usually be thought of as passing instructions for it to perform. Have you ever tried crochet? Well in crochet you're the computer, performing a series of instructions from a pattern (a program). This was an excellent talk from Lily Madar (&lt;a href="https://mobile.twitter.com/Lily_2point0" rel="noopener noreferrer"&gt;@Lily_2point0&lt;/a&gt;) who lead us through the uncanny similarities between wrestling yarn and massaging code.&lt;/p&gt;

&lt;p&gt;The talk, titled &lt;em&gt;"Programming with Yarn"&lt;/em&gt; (not the package manager), drew many parallels between the worlds of programming and crochet. Be that Ravelry for GitHub-like code sharing, forking a pattern to customise it for yourself or the event loop of crochet providing unit-testing confidence that you've not made a mistake the line prior. &lt;/p&gt;

&lt;p&gt;Depending on where you sit you might argue that programming is as much a craft as it is a science. I have always admired the "craft" of the profession, sure there are plenty of strict rules that must be followed but, like crochet, what you create with them is endless. &lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1002232071597719552-565" src="https://platform.twitter.com/embed/Tweet.html?id=1002232071597719552"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1002232071597719552-565');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1002232071597719552&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;The next talk started out with a warning, things were going to be a bit grim. &lt;em&gt;"Working towards a greener world from behind the keyboard"&lt;/em&gt; was an honest look at the state of our industry and the climate impact. Natalia Waniczek (&lt;a href="https://twitter.com/lil_natw" rel="noopener noreferrer"&gt;@lil_natw&lt;/a&gt;) started with the news that the U.N. believes with have "10 years remaining" (to avoid climate catastrophe). CO2 has returned to pre-pandemic levels. &lt;/p&gt;

&lt;p&gt;Did you know that the internet is now responsible for &lt;strong&gt;3.7%&lt;/strong&gt; of worldwide CO2 emissions? Approximately the output of Germany, or global air travel, is used to power the modern world. Perhaps that's not as much but put this into perspective; streaming an album on Spotify, 8hr/day, for a month produces the same amount of emissions as a car driving &lt;strong&gt;31.2km&lt;/strong&gt;. After a certain point CDs become the economical option 🤯.&lt;/p&gt;

&lt;p&gt;Solving the issue of climate change is increasingly feeling like it's out of out reach, with the big corporations and fossil fuel industries shouldering the blame however, there are things we can still do to make a small difference, even in the world of frontend. There are "green" &lt;a href="https://websitecarbonfootprint.com/green-web-hosts/" rel="noopener noreferrer"&gt;hosting providers&lt;/a&gt; and you can check your site with a &lt;a href="https://www.websitecarbon.com" rel="noopener noreferrer"&gt;Carbon Calculator&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;We're increasingly sending more and more data over the wire to our users (remember those Spotify streams?) so saving bytes on your JavaScript bundles not only helps the user but also the planet. One thing that got me thinking was the choice of language, presumably the closer we are to machine code the more efficient we become, thus less waste. Do we always have to reach for a higher-level language? Could we use WebAssembly to build even more efficient websites?   &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;16:15&lt;/strong&gt; &lt;em&gt;Sareh and Ruth&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;After a quick ice cream break and another #mingle we were entering the final section of the day. Sareh Heidari (&lt;a href="https://twitter.com/sareh88" rel="noopener noreferrer"&gt;@sareh88&lt;/a&gt;) spoke to some of the reasons that this digital future isn't for everyone and the folks we're leaving behind in her &lt;em&gt;"Digital Exclusion in Healthcare"&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;The COVID-19 pandemic was a hugely stressful time for many and between the frequent lockdowns in the UK the hope of normalcy rested on access to a vaccine. The rollout varied between countries but in the UK NHS was fairly quick to get it to those in most need and as COVID-19 seemed to be the most fatal to the elderly, they were first in line.&lt;/p&gt;

&lt;p&gt;However, of the ~60 million people in the UK, only ~20 million appointments were booked online. Huge numbers were booked online but it still represents only 1/3 of the population. Who did we miss? Sareh's talk expanded on the various reasons for people and groups that don't or can't access digital services. &lt;/p&gt;

&lt;p&gt;The idea that "Digital-first shouldn't mean digital-only" really struck me as one of those things that should be obvious but as someone who builds websites I often start out with the assumption that access is given - that's not always true. &lt;/p&gt;

&lt;p&gt;There was plenty to think about from this talk, both the NHS simplifying its language down to "pee &amp;amp; poo" and the &lt;a href="https://thedecisionlab.com/reference-guide/organizational-behavior/the-com-b-model-for-behavior-change" rel="noopener noreferrer"&gt;COM-B model&lt;/a&gt; are highlights. The NHS in particular is always something I valued and, from a frontend point of view, it is great to see them leading the way with their &lt;a href="https://digital.nhs.uk/about-nhs-digital/corporate-information-and-documents/nhs-digital-style-guidelines" rel="noopener noreferrer"&gt;NHS Digital Styleguide&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;A question that stuck with me; why is access to NHS digital services not free? Zero-rating (where providers don't charge for access) was talked about during COVID but feels like a no-brainer to me. We should break down as many barriers preventing people's access to healthcare. &lt;/p&gt;

&lt;p&gt;Check out the excellent &lt;a href="https://www.goodthingsfoundation.org/what-we-do/" rel="noopener noreferrer"&gt;Good Things Foundation&lt;/a&gt; which works at bridging the digital device. &lt;/p&gt;

&lt;p&gt;To bring the day to a close we had "Day Disco" by Ruth John &lt;a href="https://twitter.com/rumyra" rel="noopener noreferrer"&gt;@rumyra&lt;/a&gt;. An excellent showcase of all the media APIs available on the modern web; interactive animations drawn to canvas, bouncing to the beat. &lt;/p&gt;

&lt;p&gt;I'm not all too familiar with those APIs to be completely honest but now I wanna try embedding some music into my personal site just to give them a go, why not? For my day-to-day the MediaSession API sounded like it might solve an issue I was seeing with Spaces, this API provides the metadata needed to show album art, track info etc in the browser playing overlays.&lt;/p&gt;

&lt;p&gt;A great talk to round out the day! 🥁 🎉 🗣 ️&lt;/p&gt;

&lt;p&gt;&lt;a href="/public/2022-11-13/three-amigos.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="/public/2022-11-13/three-amigos.jpeg" title="Ciaran, Alex and me, sitting in the comfy red seats smiling at the camera at the end of the conf" alt="Three amigos"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;17:30&lt;/strong&gt; &lt;em&gt;closing&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Throughout the talks, I looked for a pattern, the common thread that linked them. The overriding "frontend" aspect was clear from the outset. However there's a nice compliment to the individual talks and their ideas; those of inclusion and empowerment; thoughtful design with the Users and Communities at the heart of it; exploitation and climate. &lt;/p&gt;

&lt;p&gt;Huge props to the organisers, the AV staff, and the folks keeping the coffee flowing - you all did an amazing job. Special shout-out to the dude doing the captions, he certainly had a bunch of technical vocab to deal with (though in talking to him I learnt, it's Greek / Roman philosophers that trip him up more than often.)&lt;/p&gt;

&lt;p&gt;Overall I left the day feeling invigorated, I had talked to new people and listened to many brilliant talks. I'm left with many questions and many, many, ideas! I can't wait to get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;18:15&lt;/strong&gt; &lt;em&gt;afters&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Before I headed back to London there was one last chance to say hey and meet some more conference-goers. This time in the bar. Always good to close off the night with cheers to new friends. The beachside bar didn't disappoint. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2022-11-13%2Fbeach.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2022-11-13%2Fbeach.jpeg" title="Photo of the beach late at night, the waves are all sorts of purple and pink from the light of the pier on the right there are many pin-pricks of light that come from the famous offshore wind" alt="Beach" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See you next year for, ffconf.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;First appeared on &lt;a href="https://blog.barnettjones.com/2021/03/14/from-hero-to-zeroseg/" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;, published 29th Nov 2022&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>frontend</category>
      <category>conference</category>
      <category>ffconf</category>
    </item>
    <item>
      <title>From Hero to Zero(seg)</title>
      <dc:creator>Matt 🦕</dc:creator>
      <pubDate>Mon, 15 Mar 2021 14:14:11 +0000</pubDate>
      <link>https://dev.to/mattjbones/from-hero-to-zero-seg-b5j</link>
      <guid>https://dev.to/mattjbones/from-hero-to-zero-seg-b5j</guid>
      <description>&lt;p&gt;This weekend I spent some time getting to know how Raspberry Pi, GPIO and SPI work - not know what any of that means? Great, me neither until this weekend.&lt;/p&gt;

&lt;p&gt;For a tenner I picked up a &lt;a href="https://thepihut.com/products/zeroseg"&gt;ZeroSeg&lt;/a&gt; with the aim to use it to display numbers like temperature, time, and anything else you can think of. I often browse The PiHut just to see what's available and what sparks ideas for little projects like this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kit
&lt;/h2&gt;

&lt;p&gt;For this build I used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://thepihut.com/products/zeroseg"&gt;ZeroSeg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Soldering Iron (etc)&lt;/li&gt;
&lt;li&gt;Raspberry Pi 3B+&lt;/li&gt;
&lt;li&gt;Micro SD (or USB thumb)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Hands on
&lt;/h3&gt;

&lt;p&gt;The ZeroSeg comes as a kit that needs building with a couple of resistors and chips that need soldering to the supplied board - loads of fun and a great project for beginners as it comes with a pretty comprehensive &lt;a href="https://cdn.shopify.com/s/files/1/0176/3274/files/ZeroSeg_User_Guide_1.2.pdf"&gt;guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bWQsK4Bq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2021-03-14/soldering.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bWQsK4Bq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2021-03-14/soldering.jpeg" alt="Soldering the ZeroSeg" title="ZeroSeg board on a plastic mat with some pliers nearby - solding in progress" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Prepping the Pi
&lt;/h3&gt;

&lt;p&gt;I've covered my go-to &lt;a href="https://blog.barnettjones.com/2020/05/10/raspberry-pi-quick-guide/"&gt;Raspberry Pi setup&lt;/a&gt; previously and this build is no different, DietPi flashed onto a USB thumbdrive (booting to USB supported on Raspberry Pi 3 B+ onwards).&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing, Testing
&lt;/h3&gt;

&lt;p&gt;To get things started I popped the ZeroSeg on the &lt;a href="https://en.wikipedia.org/wiki/General-purpose_input/output"&gt;GPIO&lt;/a&gt; pins.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;GPIO is simply an series of pins that can be used to talk (send signals) to any number of devices, sensors etc via the Raspberry Pi - switching off / on an LED is common example you'll often see.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next I used SSH to connect to my Raspberry Pi so I can configure the display without having to plug into a mouse and keyboard, instead working on my laptop.&lt;/p&gt;

&lt;p&gt;SSH gives me a command line on the Raspberry Pi and if you've not tried using the command line I highly recommend it - it's a great way to interact with different devices from your Mac / Windows PC to a Cloud Server / Raspberry Pi.&lt;/p&gt;

&lt;p&gt;There was a recommended library in the ZeroSeg guide for interacting with the display which I &lt;a href="https://github.com/AverageMaker/ZeroSeg.git"&gt;cloned&lt;/a&gt; and had a play with.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/523511294" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Ok, What Next?
&lt;/h2&gt;

&lt;p&gt;The library provided uses Python, which is a typical programming language for this type of project. However my plan was to make the display available over the network and I wanted to do this using &lt;a href="https://nodejs.dev"&gt;NodeJS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are a couple of benefits to NodeJS, first you write JavaScript which is a language I'm already comfortable with and secondly, there are a wealth of packages on &lt;a href="https://www.npmjs.com"&gt;npm&lt;/a&gt; that I can use to get something workable quickly.&lt;/p&gt;

&lt;p&gt;I decided to try find a NodeJS equivalent package / library (over on &lt;code&gt;npm&lt;/code&gt;) and then write a small wrapper to expose it to the web. (I probably could have just done it all in Python and saved myself an afternoon, but, the learning was fun.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Abandonware
&lt;/h3&gt;

&lt;p&gt;I started my search with "node raspberry-pi" and "node zeroseg" before finding something with "node MAX7219" (where MAX7219 is the name of the chip powering the ZeroSeg).&lt;/p&gt;

&lt;p&gt;Turns out there's no "recent" library that does what I want. Several libraries claimed to talk to the chip however I had some real issues &lt;a href="https://github.com/sebleedelisle/rpi-led-control"&gt;getting things going&lt;/a&gt; on my Pi.&lt;/p&gt;

&lt;p&gt;Through this searching I did learn the ZeroSeg uses the &lt;a href="https://en.wikipedia.org/wiki/Serial_Peripheral_Interface"&gt;Serial Peripheral Interface&lt;/a&gt;(SPI) to talk to to the display and that support for SPI in NodeJS is &lt;a href="https://www.npmjs.com/package/spi"&gt;lacking&lt;/a&gt; - knowing what to search for however, "node spi", for made a huge difference when debugging (and reading the manual more thoroughly would have helped too).&lt;/p&gt;

&lt;h3&gt;
  
  
  Bespokeware
&lt;/h3&gt;

&lt;p&gt;However after a bit of digging I discovered both &lt;a href="https://www.npmjs.com/package/spi-device"&gt;spi-device&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/max7219"&gt;MAX7219.js&lt;/a&gt; which combined could do what I wanted.&lt;/p&gt;

&lt;p&gt;The result is this: a new &lt;a href="https://github.com/mattjbones/MAX7219.js"&gt;MAX7219.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I forked &lt;code&gt;MAX72219.js&lt;/code&gt; from the original repo and converted to use &lt;code&gt;spi-device&lt;/code&gt;. It was a super interesting deep dive into how SPI works and how to talk to devices attached to a Raspberry Pi.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's See it in Action?
&lt;/h3&gt;

&lt;p&gt;Ok, so now I have a NodeJS interface to the display which means I can start to have a little play. In the update to &lt;code&gt;MAX7219.js&lt;/code&gt; I started adding some abstractions (helper functions for common actions like &lt;code&gt;setText&lt;/code&gt;) on top of the &lt;code&gt;setDigitSymbol&lt;/code&gt; function - which will change on of the 8 numbers to the supplied symbol.&lt;/p&gt;

&lt;p&gt;To demonstrate some of the new abstractions I built I created an &lt;code&gt;examples&lt;/code&gt; folder and started putting together things like &lt;a href="https://github.com/mattjbones/MAX7219.js/blob/master/examples/show_time.js"&gt;show_time.js&lt;/a&gt; and &lt;a href="https://github.com/mattjbones/MAX7219.js/blob/master/examples/show_message.js"&gt;show_message.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The one I'm running at the moment is &lt;a href="https://github.com/mattjbones/MAX7219.js/blob/master/examples/bounce.js"&gt;bounce.js&lt;/a&gt; - which "bounces" the time between the "sides" of the display, check it out 👇&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/523511312" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This feels like a good place to have reached, I was able to talk to my display, with NodeJs, and I even made some commits to GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;As mentioned I want to make this display available to devices on the network so the first thing I will do is implement some wrapper around my library so that I can do something like &lt;code&gt;http://my-zeroseg.home/setText?value="hello_world"&lt;/code&gt; and have it shown on the display.&lt;/p&gt;

&lt;p&gt;Another thing that I learned through the course of the project is the &lt;a href="https://www.arduino.cc"&gt;Arduino&lt;/a&gt; board should also be able to talk to this display - so I might try the whole thing again but this time without the Pi.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;First appeared on &lt;a href="https://blog.barnettjones.com/2021/03/14/from-hero-to-zeroseg/"&gt;my blog&lt;/a&gt;, published 15th May 2021&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>raspberrypi</category>
      <category>node</category>
      <category>showdev</category>
      <category>gpio</category>
    </item>
    <item>
      <title>Setup Guide for Plex on Raspberry Pi</title>
      <dc:creator>Matt 🦕</dc:creator>
      <pubDate>Thu, 26 Nov 2020 19:39:33 +0000</pubDate>
      <link>https://dev.to/mattjbones/setup-guide-for-plex-on-raspberry-pi-153c</link>
      <guid>https://dev.to/mattjbones/setup-guide-for-plex-on-raspberry-pi-153c</guid>
      <description>&lt;p&gt;Media streaming isn't a new thing and since the early days of the internet home media setups have been fairly common. What has changed is the hardware needed powering such a setup. The RasperryPi 4 is a powerful, pocket-sized, computer which can be setup as a home media server with very little cost or effort.&lt;/p&gt;

&lt;p&gt;For this setup you'll need a RaspberryPi (4 if possible), a microSD card and an external drive of your media.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2020-11-26%2Fsetup.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2020-11-26%2Fsetup.JPG" title="RaspberryPi, SD card, Thumbdrive and dongle" alt="Starting simple"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps
&lt;/h2&gt;

&lt;p&gt;1. Flash SD Card with DietPi&lt;/p&gt;

&lt;p&gt;2. Configure DietPi Automations&lt;/p&gt;

&lt;p&gt;3. First boot&lt;/p&gt;

&lt;p&gt;4. Configure &lt;a href="http://plex.tv" rel="noopener noreferrer"&gt;Plex&lt;/a&gt; and &lt;a href="https://tautulli.com" rel="noopener noreferrer"&gt;Tautulli&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Flash SD Card with DietPi
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dietpi.com" rel="noopener noreferrer"&gt;DietPi&lt;/a&gt; is my goto image when I'm working on RaspberryPi projects (I've written a little about this &lt;a href="https://blog.barnettjones.com/2020/05/10/raspberry-pi-quick-guide" rel="noopener noreferrer"&gt;in the past&lt;/a&gt; as it offers a great base image, a simple software configuration and a ton of guides in &lt;a href="https://dietpi.com/docs/" rel="noopener noreferrer"&gt;their docs&lt;/a&gt; which is super helpful for getting started.&lt;/p&gt;

&lt;p&gt;Head over to the website and download the DietPi image for your device (there are lots of platforms on offer). Each version of the RaspberryPi is catered for in one image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2020-11-26%2Fdiet-pi-image.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2020-11-26%2Fdiet-pi-image.png" title="Screenshot of the DietPi RaspberryPi image" alt="DietPi image for RaspberryPi"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Flashing the SD card can be done a number of different ways though I prefer the command line and there's a great guide on &lt;a href="https://www.raspberrypi.org/documentation/installation/installing-images/mac.md" rel="noopener noreferrer"&gt;raspberrypi.com&lt;/a&gt; for macOS (and other plaforms).&lt;/p&gt;

&lt;h3&gt;
  
  
  Command Line
&lt;/h3&gt;

&lt;p&gt;In short, use &lt;code&gt;diskutil list&lt;/code&gt; to find the SD card, then unmount with &lt;code&gt;diskutil unmountDisk /dev/diskN&lt;/code&gt; where &lt;code&gt;diskN&lt;/code&gt; is the SD card. e.g&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DietPi_RPi-ARMv6-Buster-3 % diskutil list
/dev/disk0 (internal, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *500.3 GB   disk0
   1:                        EFI ⁨EFI⁩                     314.6 MB   disk0s1
   2:                 Apple_APFS ⁨Container disk1⁩         498.7 GB   disk0s2
   3:       Apple_KernelCoreDump ⁨⁩                        1.3 GB     disk0s3

/dev/disk1 (synthesized):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      APFS Container Scheme -                      +498.7 GB   disk1
                                 Physical Store disk0s2
   1:                APFS Volume ⁨Macintosh HD - Data⁩     333.7 GB   disk1s1
   2:                APFS Volume ⁨Preboot⁩                 428.0 MB   disk1s2
   3:                APFS Volume ⁨Recovery⁩                1.2 GB     disk1s3
   4:                APFS Volume ⁨VM⁩                      4.3 GB     disk1s4
   5:                APFS Volume ⁨Macintosh HD⁩            23.8 GB    disk1s5
   6:              APFS Snapshot ⁨com.apple.os.update-...⁩ 23.8 GB    disk1s5s1

/dev/disk2 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *63.9 GB    disk2
   1:             Windows_FAT_32 ⁨boot⁩                    268.4 MB   disk2s1
   2:                      Linux ⁨⁩                        63.6 GB    disk2s2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my case this is &lt;code&gt;disk2&lt;/code&gt; as the other disks are APFS macOS Volumes and contain boring things like the operating system and my photos.&lt;/p&gt;

&lt;p&gt;Next we need to unmount that disk and then copy the image (I've openned terminal inside the folder of the DietPi image)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;diskutil unmountDisk /dev/disk2
sudo dd bs=1m if=DietPi_RPi-ARMv6-Buster.img of=/dev/rdisk2; sync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have issues running this command the &lt;a href="https://www.raspberrypi.org/documentation/installation/installing-images/mac.md" rel="noopener noreferrer"&gt;RaspberryPi guide&lt;/a&gt; has some suggestions.&lt;/p&gt;

&lt;h3&gt;
  
  
  balenaEtcha
&lt;/h3&gt;

&lt;p&gt;Alternatively &lt;a href="https://dietpi.com/docs/user-guide_installation/" rel="noopener noreferrer"&gt;DietPi&lt;/a&gt; recommends using balenaEtcha to flash the drive which offers a more friendly UI to select images, disks and flashes the SD card for you.&lt;/p&gt;

&lt;p&gt;Either balenaEtcha or the command line should result in a flashed SD card that looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2020-11-26%2Fflashed-sd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2020-11-26%2Fflashed-sd.png" alt="Contents of the flashed DietPi SD"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the fresh DietPi SD card we're in a good position to plug in a monitor, keyboard and mouse, then start up the Pi. If you are curious and want to try configuring it for yourself then try it out, you'll be taken through the DietPi setup and their helpful software configuration.&lt;/p&gt;

&lt;p&gt;However I much prefer to automate as much of the first setup as possible, it's far more reliable and requires less fiddling with DietPi. &lt;/p&gt;

&lt;h2&gt;
  
  
  Configure DietPi Automations
&lt;/h2&gt;

&lt;p&gt;When I say automate I mean that we're going to configure the DietPi image with a baseline set of software (on top of what is already provided). To do this we need to add a few lines to the &lt;code&gt;/boot/dietpi.txt&lt;/code&gt; file on the SD card we just flashed. We can also pre-configure Wifi too however for the purpose of this setup I'm assuming wired ethernet but the approach is similar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding software
&lt;/h3&gt;

&lt;p&gt;If you open the &lt;code&gt;dietpi.txt&lt;/code&gt; you'll find a load of configuration options and to make things easier you'll probably find &lt;code&gt;⌘ + F&lt;/code&gt; is your friend here. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I recommend copying &lt;code&gt;dietpi.txt&lt;/code&gt; to your desktop then making the changes there, don't forget to copy it back onto the SD card when you're done though.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From &lt;a href="https://github.com/MichaIng/DietPi/wiki/DietPi-Software-list" rel="noopener noreferrer"&gt;this list&lt;/a&gt; we need to find the corresponding codes for Plex (42), Tautulli (146), RPi Monitor (66). We'll also need some utilities to read exFAT drives (more on that later). &lt;/p&gt;

&lt;p&gt;Add a &lt;code&gt;AUTO_SETUP_INSTALL_SOFTWARE_ID&lt;/code&gt; line for each softare you're configuring, e.g:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

# Software to automatically install
# - List of available software IDs: https://github.com/MichaIng/DietPi/wiki/DietPi-Software-list
# - Add as many entries as you wish, one each line.
# - DietPi will automatically install all pre-reqs (e.g. ALSA/X11 for desktops etc)
# - E.g. the following (without the leading "#") will install the LXDE desktop automatically on first boot:
#AUTO_SETUP_INSTALL_SOFTWARE_ID=23
#Plex
AUTO_SETUP_INSTALL_SOFTWARE_ID=42

#Tautulli
AUTO_SETUP_INSTALL_SOFTWARE_ID=146

#RPi-Mon
AUTO_SETUP_INSTALL_SOFTWARE_ID=66

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These changes will automatically install the above software when DietPi runs its first time setup.&lt;/p&gt;

&lt;p&gt;Next we need to create a small install script to allow the Pi to read from exFAT and other windows partitions (we're likely adding an external harddrive which will have some flavour of Windows filesystem). To do this we need to create a text file called &lt;code&gt;Automation_Custom_Script.sh&lt;/code&gt; on the &lt;code&gt;/boot/&lt;/code&gt; drive. The script will only have one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash
apt-get install -y exfat-fuse exfat-utils ntfs-3g 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To enable this setup script we again need to modify &lt;code&gt;dietpi.txt&lt;/code&gt;, find &lt;code&gt;AUTO_SETUP_CUSTOM_SCRIPT_EXEC&lt;/code&gt; and change it to 1, e.g:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Custom Script (post-networking and post-DietPi install)
# - Allows you to automatically execute a custom script at the end of DietPi install.
# - Option 0 = Copy your script to /boot/Automation_Custom_Script.sh and it will be executed automatically.
# - Option 1 = Host your script online, then use e.g. AUTO_SETUP_CUSTOM_SCRIPT_EXEC=https://myweb.com/myscript.sh and it will be downloaded and executed automatically.
# - Executed script log: /var/tmp/dietpi/logs/dietpi-automation_custom_script.log
AUTO_SETUP_CUSTOM_SCRIPT_EXEC=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally we need to let DietPi know to use all our automations so, find &lt;code&gt;AUTO_SETUP_AUTOMATED&lt;/code&gt; and change it to 1.&lt;/p&gt;

&lt;p&gt;You can download both &lt;a href="https://blog.barnettjones.com/public/2020-11-26/dietpi.txt" rel="noopener noreferrer"&gt;dietpi.txt&lt;/a&gt; and &lt;a href="https://blog.barnettjones.com/public/2020-11-26/Automation_Custom_Script.sh" rel="noopener noreferrer"&gt;Automation_Custom_Script.sh&lt;/a&gt; if you please.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finishing Touches
&lt;/h3&gt;

&lt;p&gt;There are a bunch of other options for the setup of the system so feel free to have a play. One line which might be worth changing is &lt;code&gt;AUTO_SETUP_NET_HOSTNAME&lt;/code&gt; which defaults to "DietPi".&lt;/p&gt;

&lt;h2&gt;
  
  
  First Boot
&lt;/h2&gt;

&lt;p&gt;If you've decided to setup the automations from the previous section then this step should be seamless. Plug you SD card into your pi, make sure there's ethernet, and the power it up. The setup process can take a bit of time but after about ~10 minutes you should be able to visit the IP address of the Pi on port 8888, e.g. &lt;code&gt;192.168.0.151:8888&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you're impatient / worried, like I was, you can always try SSH'ing to the Pi to make sure it's behaving &lt;code&gt;ssh dietpi@192.168.0.153&lt;/code&gt; with the password &lt;code&gt;dietpi&lt;/code&gt;. You should see something that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2020-11-26%2Fimpatient-pi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2020-11-26%2Fimpatient-pi.png" title="Image of the console ouput from SSH while DietPi is doing its first setup" alt="Impatient Pi"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After some time the setup should complete, once you're able to visit the RPI-Mon page (&lt;code&gt;192.168.0.153:8888&lt;/code&gt; in my case) you should be able to move onto the next, and final, step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2020-11-26%2Fdiet-pi-mon.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.barnettjones.com%2Fpublic%2F2020-11-26%2Fdiet-pi-mon.png" title="Image of the PiMon for the new DietPi instalation" alt="DietPiMon status"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's worth SSH'ing into the Pi to check the first time setup has completed too (otherwise it will reboot as you're configuring the other services)&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure Plex and Tautulli
&lt;/h2&gt;

&lt;p&gt;We can configure Plex and Tautulli. To do so we simply need to hit the local webservers for each of these services i.e the local webservers we just installed on the Pi. &lt;/p&gt;

&lt;p&gt;For Plex visit:  &lt;code&gt;http://192.168.0.151:32400/web/index.html&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;For Tautulli visit: &lt;code&gt;http://192.168.0.151:8181&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;(You IP address is likely to be different)&lt;/p&gt;

&lt;p&gt;During the Plex setup it will ask you to add a library for your media, at this point you should connect your USB drives and click &lt;code&gt;Browse&lt;/code&gt;. What you should see is a bunch of directories none of which are your drive, why? Well Linux needs to mount USB drives to make them available to the operating system.&lt;/p&gt;

&lt;p&gt;Ideally this would be handled automatically, howecer I've yet to find a good solution for this, though &lt;code&gt;usbmount&lt;/code&gt; looks promising.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mounting Drives for Plex
&lt;/h3&gt;

&lt;p&gt;To mount a drive on the Pi first SSH over to the machine &lt;code&gt;ssh dietpi@192.168.0.151&lt;/code&gt; with the password &lt;code&gt;dietpi&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once logged in we first need to create a folder for the drive to be mounted, run &lt;code&gt;sudo mkdir /media/my_drive&lt;/code&gt; to create the folder. Next we need to mount the actual drive, run &lt;code&gt;sudo fdisk -l&lt;/code&gt; and look for your drive (hint &lt;code&gt;Disk model&lt;/code&gt; is usually provied for USB drives).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Disk /dev/sda: 57.3 GiB, 61530439680 bytes, 120176640 sectors
Disk model: Ultra Fit       
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xbba82a87

Device     Boot Start       End   Sectors  Size Id Type
/dev/sda1          32 120176639 120176608 57.3G  7 HPFS/NTFS/exFAT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you've identified the drive we need to instruct linux where to mount it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mount /dev/sda1 /media/my_drive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All things being well the Pi should mount your drives and they will be available under &lt;code&gt;/media/&lt;/code&gt; when you go back to Plex (refresh first).&lt;/p&gt;

&lt;p&gt;Make sure to login with your Plex account, if you're lucky you'll be able to configure remote access (the port forwarding needed differs across routers but some will do it automatically.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Tautulli
&lt;/h3&gt;

&lt;p&gt;Once your Plex server is up and running you can start Tautulli. &lt;/p&gt;

&lt;p&gt;Tautulli is a service that look at all the Plex server metadata to give you an overview of how much media is being watched, by whom and on what device - especially useful when sharing your Plex server with friends and family.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sit back and enjoy
&lt;/h2&gt;

&lt;p&gt;That's it, you should now be able to watch your media anywhere via one of the many Plex apps!&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this guide, I would like it to be an entirely plug and play experience with the USB drives and I suspect I will do a followup post when I figure that part out. Feel free to reach me on Twitter with any feedback and enjoy your Pi powered media server.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;First appeared on &lt;a href="https://blog.barnettjones.com/2020/11/26/dietpi-plex-setup/" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;, published 26th November 2020&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>raspberrypi</category>
      <category>plex</category>
      <category>dietpi</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Homebridge x Xiaomi Roborock S5 Vacuum setup guide</title>
      <dc:creator>Matt 🦕</dc:creator>
      <pubDate>Mon, 31 Aug 2020 23:10:24 +0000</pubDate>
      <link>https://dev.to/mattjbones/homebridge-x-xioami-roborock-s5-vacuum-setup-guide-3gfj</link>
      <guid>https://dev.to/mattjbones/homebridge-x-xioami-roborock-s5-vacuum-setup-guide-3gfj</guid>
      <description>&lt;p&gt;I'm super into home automation and I have built a fairly decent setup around Apple Home. &lt;/p&gt;

&lt;p&gt;I’m completely sold on being able to control my house by voice, asking Siri to turn on various lights or ask about air the quality. &lt;/p&gt;

&lt;p&gt;There is one holdout however, one that I can’t control by voice, my robot vacuum cleaner. HomeKit doesn’t support vacuum cleaners so we’ll have to get creative.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;There are a bunch of good resources (links below) out there already for how to do this but I wanted to share my steps as it involved a bit of trial and error.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;(&lt;a href="https://homebridge.io"&gt;Homebridge&lt;/a&gt; setup and configured with HomeKit)&lt;/li&gt;
&lt;li&gt;Get Xiaomi device token

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://bluestacks.com/"&gt;BlueStacks&lt;/a&gt; (or any Android emulator)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.apkmirror.com/apk/xiaomi-inc/mihome/mihome-5-4-49-release/"&gt;MiHome 4.4.49&lt;/a&gt; (specifically this version)&lt;/li&gt;
&lt;li&gt;Login, extract log and get token (stored in &lt;a href="http://bitwarden.com"&gt;Bitwarden&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Configure &lt;a href="https://github.com/aholstenson/miio"&gt;miio&lt;/a&gt; device w/ token + ip

&lt;ul&gt;
&lt;li&gt;Test it all works&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Configure Homebridge accessory (the robot vacuum)

&lt;ul&gt;
&lt;li&gt;Homebridge plugin: &lt;a href="https://www.npmjs.com/package/homebridge-xiaomi-roborock-vacuum"&gt;homebridge-xiaomi-roborock-vacuum&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add accessory: &lt;code&gt;XiaomiRoborockVacuum&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Test in Apple Home&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Configure rooms

&lt;ul&gt;
&lt;li&gt;Add timer (for all rooms configured) 12:00am / 00:00&lt;/li&gt;
&lt;li&gt;Add room setup in order selected in timer&lt;/li&gt;
&lt;li&gt;Restart Homebridge&lt;/li&gt;
&lt;li&gt;Check each room (order may vary)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Homebrige
&lt;/h2&gt;

&lt;p&gt;If you're reading this guide there's a good chance you know about Homebridge already. For those who don't Homebridge allows for non-HomeKit devices to be added to you Apple Home and there's a good guide available &lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/brandonb927" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iiRsec5F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--cRRvYjy5--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/17928/a24e2b52-4d76-414e-bf20-334641666550.jpg" alt="brandonb927"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/brandonb927/getting-started-with-homebridge-on-a-raspberry-pi-zero-w-242h" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Getting started with Homebridge on a Raspberry Pi Zero W&lt;/h2&gt;
      &lt;h3&gt;Brandon Brown ・ Mar 24 '18 ・ 5 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#raspberrypi&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#homebridge&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;I'll not go too much into the details for Homebridge but just for context I have Homebridge running on a Raspberry Pi and added to my Apple home.&lt;/p&gt;

&lt;h2&gt;
  
  
  Xiaomi Device Token
&lt;/h2&gt;

&lt;p&gt;The first hurdle to overcome with this setup is getting the Xiaomi device token which is used to communicate with the vacuum. The vacuum in question is the Xiaomi Roborock S5 Vacuum.&lt;/p&gt;

&lt;p&gt;I assume the device token is a layer of security added to ensure only Xiaomi clients can talk to the device and without it setting up the Homebridge wouldn't be possible.&lt;/p&gt;

&lt;p&gt;So how do we get access to this secure token?&lt;/p&gt;

&lt;h3&gt;
  
  
  Android
&lt;/h3&gt;

&lt;p&gt;The simplest way to do this is by getting hold of an Android device and run an old version of the Xiaomi MiHome app (4.4.49 to be precise). This version of the MiHome app helpfully prints the device details to a log file which can be extracted and read.&lt;/p&gt;

&lt;p&gt;As an Apple household we don't have any Android devices so that's where &lt;a href="https://bluestacks.com"&gt;BlueStacks&lt;/a&gt; comes in. BlueStacks is a powerful emulator aimed at Android gamers who want to play on macOS or Windows 10 and best of all, it's free to download.&lt;/p&gt;

&lt;p&gt;Once downloaded, install the MiHome APK and sign in to to the Mi account where the vacuum is registered. What should happen in the background is a file will be created that you can export to you machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sdcard/SmartHome/logs/plug_DeviceManager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking inside this file you will find a bunch of log messages and a few lines of JSON which will contain the string &lt;code&gt;"token":...&lt;/code&gt; verify the device is the correct one, as any device on the account will show up here. It's also a good idea to note down the &lt;code&gt;deviceId&lt;/code&gt; and &lt;code&gt;ip&lt;/code&gt; at this point too as you might need them later.&lt;/p&gt;

&lt;p&gt;Once you have your token(s) store them somewhere for safekeeping, a notepad is fine for this but I used a &lt;a href="https://bitwarden.com"&gt;Bitwarden&lt;/a&gt; secure note becuase I was moving between computers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure &lt;code&gt;miio&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The first thing to do with the token is to test it works.&lt;/p&gt;

&lt;p&gt;In order to test this use &lt;code&gt;miio&lt;/code&gt;, which provides a slew of useful commands that help with talking to various MiHome devices. Install this on the computer where Homebridge is installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g miio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that's installed the command &lt;code&gt;miio discover&lt;/code&gt; can be used to quickly scan the network for devices (if you run this now you might see your vacuum, however it's likely the token will come back as &lt;code&gt;???&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;To actually talk to the target device the local token cache needs to be updated with the one retrieved from the log. To do this simply run the command &lt;code&gt;miio tokens update&lt;/code&gt; with the IP and token for the device&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;miio tokens update 192.168.0.12 my-super-secret-token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will allow you to then query the device with &lt;code&gt;miio inspect&lt;/code&gt; which should return various device stats, state, etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ miio inspect 192.168.0.12
 INFO  Attempting to inspect 192.168.0.12

Device ID: ________
Model info: roborock.vacuum.s5
Address: 192.168.0.12
Token: my-super-secret-token via stored token

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point you should be fairly confident the token is valid and the device is responding. Nice! 👍&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure Homebridge
&lt;/h2&gt;

&lt;p&gt;The way different devices / accessories are configured in Homebridge is by plugins. The plugin for setting up the vacuum is the &lt;a href="https://www.npmjs.com/package/homebridge-xiaomi-roborock-vacuum"&gt;homebridge-xiaomi-roborock-vacuum&lt;/a&gt; which you can search for on the Homebridge website or use the link.&lt;/p&gt;

&lt;p&gt;Each plugin spcifies its own accessory config and for &lt;code&gt;homebridge-xiaomi-roborock-vacuum&lt;/code&gt; the accessory &lt;code&gt;XiaomiRoborockVacuum&lt;/code&gt; has a bunch of options. The default ones are things like the &lt;code&gt;token&lt;/code&gt; and &lt;code&gt;ip&lt;/code&gt; as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  "accessory": "XiaomiRoborockVacuum",
  "name": "Robot Vacuum",
  "ip": "192.168.1.12",
  "token": "my-super-secret-token",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup is enough to start test with the Apple Home so once you've added your &lt;code&gt;ip&lt;/code&gt; and &lt;code&gt;token&lt;/code&gt; start up Homebridge and give her a whirl. 🧹&lt;/p&gt;

&lt;p&gt;The vacuum should show up in Apple Home as a fan (which sorta makes sense, at least until HomeKit supports vacuums). The power of the vacuum can be controlled by the % of the fan so &lt;code&gt;100%&lt;/code&gt; is &lt;code&gt;Turbo&lt;/code&gt; on my Roborock S5.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure Rooms
&lt;/h2&gt;

&lt;p&gt;Looking back at the config there are a few optional parameters that might be some use (&lt;a href="https://www.npmjs.com/package/homebridge-xiaomi-roborock-vacuum#optional-parameters"&gt;complete list is available&lt;/a&gt;)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Default value&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;cleanword&lt;/td&gt;
&lt;td&gt;"cleaning"&lt;/td&gt;
&lt;td&gt;Prefix name of room switches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;room&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;td&gt;Array of &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt; for room configs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;zones&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;td&gt;Array of &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;zone&lt;/code&gt; configs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When a room (or zone) Array is specified an additional switch is created in Apple Home with the name that of the entry in the array. The &lt;code&gt;cleanword&lt;/code&gt; prefixes the room name so if there was a room configured with the name "Kitchen" the switch would be labeled "cleaning Kitchen".&lt;/p&gt;

&lt;p&gt;There are 5 rooms in my flat so I setup a room config like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  "rooms": [
    { "id": 16, "name": "Bathroom" },
    { "id": 17, "name": "Kitchen" },
    { "id": 18, "name": "Bedroom" },
    { "id": 19, "name": "Hall" },
    { "id": 20, "name": "Livingroom" }
  ],
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The id is arbitrary as far as I can tell (the example config starts at 16) however when I tried starting at 1 it didn't seem to work.&lt;/p&gt;

&lt;p&gt;The next step is a little odd but there needs to be a Timer configured in the MiHome app at 00:00 / 12:00am (the first timer in the list). I suspect that the timer will setup some internal id which can be later relied on to allow for the room mapping to work reliably.&lt;/p&gt;

&lt;p&gt;When setting up the timer it's important to add room cleaning with &lt;code&gt;Select a room to divide&lt;/code&gt; and add the rooms in the order specified in the config. This should allow for the mapping from Homebridge room to MiHome room.&lt;/p&gt;

&lt;p&gt;Once again restart the Homebridge and open the Apple Home app. Test that each of the rooms are mapped correctly - don't panic if not, go through each room and make a note of where it's configured, you should find that each room is mapped and only the lables need updating.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Set the &lt;code&gt;cleanword&lt;/code&gt; to something like &lt;code&gt;Vacuum the&lt;/code&gt; so that you don't have to speak in broken sentences&lt;/li&gt;
&lt;li&gt;In Apple Home rename the device to "Robot Vacuum" or something and separte each switch into its own Tile, this helps with On / Off commands (otherwise there are a number of switches and Siri gets confused.)&lt;/li&gt;
&lt;li&gt;If you look at the main fan you should see the battery level and filter life percentages&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hey Siri, vacuum the living room
&lt;/h2&gt;

&lt;p&gt;With that you're done! Congrats, it's certainly worth it as now you should be able to trigger a room cleaning using your voice.&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>guide</category>
      <category>homebridge</category>
      <category>homeautomation</category>
    </item>
    <item>
      <title>Raspberry Pi - Quick Start</title>
      <dc:creator>Matt 🦕</dc:creator>
      <pubDate>Sun, 10 May 2020 15:12:40 +0000</pubDate>
      <link>https://dev.to/mattjbones/raspberry-pi-quick-start-1a83</link>
      <guid>https://dev.to/mattjbones/raspberry-pi-quick-start-1a83</guid>
      <description>&lt;p&gt;I love messing around with Raspberry Pis, they are a great place to try out home projects from hosting your own git server or a blog to controlling your smart windows and blocking ads. &lt;/p&gt;

&lt;p&gt;This post is mainly a reminder for me on how to get setup with a fresh SD and a Raspberry Pi. &lt;/p&gt;

&lt;h2&gt;
  
  
   Quickstart
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Download the &lt;a href="http://dietpi.com/"&gt;DietPi&lt;/a&gt; image for your device&lt;/li&gt;
&lt;li&gt;Extract the archive and then follow the &lt;a href="https://www.raspberrypi.org/documentation/installation/installing-images/"&gt;instructions&lt;/a&gt; from Raspberry Pi on how to copy the image to your SD card&lt;/li&gt;
&lt;li&gt;(&lt;em&gt;Optional&lt;/em&gt;) Pre-configure your DietPi image with WiFi / SSH details (see &lt;a href="https://dietpi.com/phpbb/viewtopic.php?p=1140#p1140"&gt;DietPi Automation&lt;/a&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why I run this setup
&lt;/h2&gt;

&lt;p&gt;I did some research to find a lightweight image for the Raspberry Pi as this is super important with low powered devices. My thinking was that I want the fewest processes running on my Pi as possible, anything that would eat into those precious CPU cycles or RAM. In most cases I am running projects which are headless (in that there is no desktop or monitor connected).&lt;/p&gt;

&lt;h3&gt;
  
  
  Minibian
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZuH04c3---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3ty1vb32ph6c1k3xyg05.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZuH04c3---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3ty1vb32ph6c1k3xyg05.png" alt="Minibian" title="Minibian Logo" width="880" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Initially I tried out &lt;a href="https://minibianpi.wordpress.com/about/"&gt;Minibian&lt;/a&gt; which is incredibly lightweight image based off the official Raspberry Pi 'Raspbian' image, this means it pretty much guaranteed to run with the Pi while being performant and stable. &lt;/p&gt;

&lt;p&gt;Minibian was perfect for hosting my blog, it meant I could install the minimum required to the run the blog, namely: SSH, Nginx and Ruby. That was it, the base image runs very little else (installing the daemon need for SSH was also a fun learning curve, n.b. if you're configuring SSHD, don't do it over SSH 🤦‍♂️).&lt;/p&gt;

&lt;h3&gt;
  
  
  DietPi
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5NkbWHwk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yuduir79igrbr40qi88p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5NkbWHwk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yuduir79igrbr40qi88p.png" alt="Diet Pi" title="DietPi Logo" width="880" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next PiProject was PiHole, I &lt;a href="https://blog.barnettjones.com/2019/03/03/something-something-pihole/"&gt;wrote a few words&lt;/a&gt; about how this works at the time but didn't detail the setup. The same thinking was applied, how can I eke out the most performance from the Raspberry Pi 1 B+ that I had handy. &lt;/p&gt;

&lt;p&gt;I found &lt;a href="https://dietpi.com"&gt;DietPi&lt;/a&gt; on a list of the "10 best Raspberry Pi images.." and while I was a little skeptical at first I gave it a go. Once again it's a pretty lightweight image, perhaps not as skinny as Minibian but still impressive. DietPi also offers a really helpful setup / configuration process which allows you to select the packages you want to install when you first run the device (and add / remove new packages later). &lt;/p&gt;

&lt;p&gt;The DietPi package management is presented in a fairly nice UI (no &lt;code&gt;apt-get&lt;/code&gt; or &lt;code&gt;yum install&lt;/code&gt; necessary) and I've not run into any problems thus far. Another benefit is the &lt;a href="https://dietpi.com/phpbb/viewtopic.php?p=1140#p1140"&gt;DietPi Automation&lt;/a&gt; which allowed me to take a fresh image and then configure it with some of the specifics of my network, namely Wifi and OpenSSH Server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other images
&lt;/h3&gt;

&lt;p&gt;I found a more recent list of &lt;a href="https://www.ubuntupit.com/best-raspberry-pi-os-available/"&gt;20 best Raspberry Pi OS&lt;/a&gt; and that had both Minibian and DietPi listed, which is nice to see. The list also includes things like Windows IoT OS which is something I'm interested in trying out. Take a look I'm sure you'll find something. &lt;/p&gt;

&lt;h2&gt;
  
  
  Where should I start?
&lt;/h2&gt;

&lt;p&gt;The Raspberry Pi is a hugely useful computing, hobby-ist, electronics, electronic device. The versatility of the Raspberry Pi means there is endless opportunities for projects and configurations. &lt;/p&gt;

&lt;p&gt;Here's one I tried as a bit of fun when the Pi Zero came out (sorry about the shakycam): &lt;br&gt;
&lt;iframe src="https://player.vimeo.com/video/416910171" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I strongly recommend just getting a familiar with linux and a Raspberry Pi project with Minibian is a great way to get going with installing packages and configuring a setup.&lt;/p&gt;

&lt;p&gt;For the really green, try setting up Raspbian on a Pi and then running it as your "dev" computer. It has a familiar-ish desktop with programs and can serve as a great middle ground. &lt;/p&gt;

&lt;p&gt;No matter where you start I'm sure you'll build something excellent!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;First appeared on &lt;a href="https://blog.barnettjones.com/2020/05/10/raspberry-pi-quick-guide/"&gt;my blog&lt;/a&gt;, published 10th May 2020&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>raspberrypi</category>
      <category>tips</category>
      <category>howto</category>
      <category>linux</category>
    </item>
    <item>
      <title>Trying out MithrilJS by Building a Browser Based Game</title>
      <dc:creator>Matt 🦕</dc:creator>
      <pubDate>Sun, 26 Apr 2020 16:44:48 +0000</pubDate>
      <link>https://dev.to/mattjbones/trying-out-mithriljs-by-building-a-browser-based-game-4n8g</link>
      <guid>https://dev.to/mattjbones/trying-out-mithriljs-by-building-a-browser-based-game-4n8g</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; I spent the weekend building a game with &lt;a href="https://mithril.js.org"&gt;MithrilJS&lt;/a&gt; and wrangling Webpack.&lt;/p&gt;

&lt;p&gt;With a lot of people rightly practising social disatancing and following stay at home orders, there's been a huge uptick in playing games remotely, be that over video chat or otherwise.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Drawful Situation
&lt;/h1&gt;

&lt;p&gt;I was in South Africa for part of their lockdown and during this time my friend group played a lot of Drawful 2, between 7 players, 3 households and 2 timezones (plus a bunch of phones / tablets).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://store.steampowered.com/app/442070/Drawful_2/"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NpcsDI8N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://steamcdn-a.akamaihd.net/steam/apps/442070/header.jpg%3Ft%3D1584634951" alt="Drawful 2 Logo" title="Drawful 2 Logo" width="460" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're not familiar with Drawful, the premise is straightforward and lots of fun - you're each given an object / scene to draw (on your phone). Then every round a drawing is shown to everyone playing and each player has to suggest what the drawing is. The drawing suggestions are then voted on with more points awarded to more accurate drawers and bonus points for sensible suggestions that aren't the right answer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R60w7BW6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2020-04-26/drawful-selection.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R60w7BW6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2020-04-26/drawful-selection.jpg" alt="Some Drawful drawings" title="1. End of an era, 2. Paranoid, 3. Speed dating" width="720" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Drawful played remotely involves a sizeable amount of tech, each house would have a device for videocalling, a device for streaming the game (over Steam Share / Twitch) and then a device per player. For players joining a Drawful game they simply need to visit a website and enter a code (nothing extra to download or pay for).&lt;/p&gt;

&lt;p&gt;With the experience of playing Drawful perculating through my mind, I wondered what else might be fun to play remotely...&lt;/p&gt;

&lt;h1&gt;
  
  
  What's the big idea?
&lt;/h1&gt;

&lt;p&gt;I really like the way Drawful (and the other Jackbox games) work with players using their devices, phones or tablets etc, to play along.&lt;/p&gt;

&lt;p&gt;I would also like to have a go at building a game. It's a chance to build something a little more fun and try out new frameworks and ways of building websites.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pitch
&lt;/h2&gt;

&lt;p&gt;I am taking inspiration from &lt;a href="https://bigpotato.com/gb/games/obama-llama/"&gt;Obama Llama&lt;/a&gt; - a great party game and combines a simple memory game with trivia questions, sharades and rhyming. Obama Llama is a series of small games all rolled into one so it's a great candidate to break into milestones and work through each over a series of weekends.&lt;/p&gt;

&lt;p&gt;A few "goals" for this project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Each milestone should be small enough to build a functional game in a weekend.&lt;/li&gt;
&lt;li&gt;There should be time left over in the weekend to do a write-up.&lt;/li&gt;
&lt;li&gt;A new technology should be used each weekend.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first milestone is going to be build out the &lt;a href="https://www.tucogames.com/memory-games/20-free-memory-films-game.html"&gt;simple memory game&lt;/a&gt; at the heart of Obama Llama.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.board-game.co.uk/obama-llama-2-review/"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a-PL8_z4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2020-04-26/obama-llama-memory.jpg" alt="Obama Llama memory game" title="Obama Llama memory game" width="880" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What are the tools?
&lt;/h1&gt;

&lt;p&gt;This weeks focus will be a single player memory game so the bulk of the progress will set the groundwork for the future. I have to decide on a JS Framework to build the website, work out what sort of build environment, deployments and then build a playable memory game.&lt;/p&gt;

&lt;h2&gt;
  
  
  Framework
&lt;/h2&gt;

&lt;p&gt;For the last 4 years I have been working with ReactJS and while it is a super interesting framework it's important to step outside your comfort zone, from time to time, and try something new.&lt;/p&gt;

&lt;p&gt;I recently read about MithrilJS on my friend, Emile's, &lt;a href="https://emilehay.xyz/making-a-case-for-the-little-guy-mithril-js/"&gt;blog&lt;/a&gt; and really like the prospect of a lightweight JS framework to build Single Page Applications (webapps).&lt;/p&gt;

&lt;p&gt;I did consider &lt;a href="https://learnvanillajs.com"&gt;VanillaJS&lt;/a&gt; and I am pretty keen to try out a vanilla JS and web-component based app but today is not that day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build Env
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Github
&lt;/h3&gt;

&lt;p&gt;First thing is to do is create a Github repo, vital to any software engineering project and will come in useful down the line when I want to think about automated deployments.&lt;/p&gt;

&lt;p&gt;Naming things is hard, and thinking about the name for this game to setup a repo was harder still. I was stuck for a decent name when my girlfriend yelled out the suggestion &lt;a href="https://github.com/mattjbones/blue_lagoon/"&gt;Blue Lagoon&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building and Serving
&lt;/h3&gt;

&lt;p&gt;I am fairly used to modern web development with the &lt;code&gt;yarn&lt;/code&gt;, &lt;code&gt;yarn start&lt;/code&gt; and &lt;code&gt;yarn build&lt;/code&gt; song and dance at this point so in an effort to keep something familiar around I opted for &lt;a href="https://webpack.js.org"&gt;Webpack&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have used webpack to build apps before but when I say "I have used webpack" I really mean that I've used &lt;code&gt;create-react-app&lt;/code&gt; or a webapp template where webpack comes pre-setup.&lt;/p&gt;

&lt;p&gt;Setting up webpack was certinaly a learning experience. I started by just trying out a few things until I got something "working". I quickly turned to &lt;a href="https://medium.com/javascript-training/beginner-s-guide-to-webpack-b1f1a3638460"&gt;various&lt;/a&gt;, &lt;a href="https://bendyworks.com/blog/getting-started-with-webpack-dev-server"&gt;resources&lt;/a&gt; and &lt;a href="https://medium.com/a-beginners-guide-for-webpack-2/index-html-using-html-webpack-plugin-85eabdb73474"&gt;blog posts&lt;/a&gt; to get things setup properly, referring to the &lt;a href="https://webpack.js.org/configuration/dev-server/"&gt;webpack docs&lt;/a&gt; frequently.&lt;/p&gt;

&lt;p&gt;After ~2 hours I had &lt;a href="https://github.com/mattjbones/blue_lagoon/commit/744cc433dc58b9cf7887d9272dae272355969810"&gt;something working&lt;/a&gt; with a folder strucutre I was happy with and the &lt;code&gt;webpack-dev-server&lt;/code&gt; serving the bundled file with an generated index.html. This was further enhanced when later when I started thinking about styling.&lt;/p&gt;

&lt;p&gt;Not too bad for a mornings work and now for the more fun part.&lt;/p&gt;

&lt;h1&gt;
  
  
  Building a game
&lt;/h1&gt;

&lt;p&gt;I haven't built much in the way of games before and I'm not really sure how to approach it other than having a picture in my head and writing down a rough idea of the game mechanics I want.&lt;/p&gt;

&lt;p&gt;For the memory game I pictured a 6x6 grid of &lt;code&gt;cards&lt;/code&gt; which, when clicked or tapped, would flip to reveal the name / picture / illustration of a pair. A second tap would reveal another card and if the pair matches they would stay flipped, if not they would flip back.&lt;/p&gt;

&lt;p&gt;There are lots of places to start with this but for me, the most interesting was to get a flipping card mechanic working.&lt;/p&gt;

&lt;h2&gt;
  
  
  You flipping what?
&lt;/h2&gt;

&lt;p&gt;Solving the flipping animation would involve solving a bunch of problems - creating components in Mithril and passing data between them, animating the component, deciding on how to do styling (plain CSS, CSS-in-JS, inline style?)... quite a bit to think about.&lt;/p&gt;

&lt;p&gt;A good place to start is with creating the components in Mirthril, this will allow me to start thinking about where animation happens and who is responsible for controlling behaviour or providing data.&lt;/p&gt;

&lt;p&gt;There is a fairly decent amount of docs over at &lt;a href="//Mithril.js.org"&gt;Mithril.js.org&lt;/a&gt; and they have a great &lt;a href="https://mithril.js.org/#components"&gt;walkthrough of how components&lt;/a&gt; are created, and work, with lots of CodePen live examples to go alongside.&lt;/p&gt;

&lt;p&gt;Learning new frameworks is fun and after a bit of fiddling I was able to commit my first &lt;a href="https://github.com/mattjbones/blue_lagoon/commit/cf56d9c51459c447d4066d4e49ed39ef29c101bc"&gt;game component&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's talk about CSS
&lt;/h3&gt;

&lt;p&gt;During my day-to-day work I use the &lt;code&gt;react-native-web&lt;/code&gt; &lt;a href="https://github.com/necolas/react-native-web/tree/master/packages/react-native-web/src/exports/StyleSheet"&gt;StyleSheet&lt;/a&gt; which is a CSS-in-JS approach to styling. React Native for Web is an abstraction layer that can used in react projects to allow the same code to be used on the web as with native (iOS / Android).&lt;/p&gt;

&lt;p&gt;I really like CSS-in-JS and find it useful to be able to see the whole of a component, style and structure, in one place. For this reason I wanted to find a similar approach for the Mithril project.&lt;/p&gt;

&lt;h4&gt;
  
  
  Mithril + j2Css
&lt;/h4&gt;

&lt;p&gt;The Mithril docs used to suggest using &lt;a href="https://mithril.js.org/archive/v1.1.6/css.html"&gt;plain CSS&lt;/a&gt; and offer a few suggestions on how to avoid some of the issues frequently occuring (in some part due to global scope). There is a section towards the end of the docs on CSS which recommend &lt;a href="https://github.com/j2css/j2c"&gt;J2C&lt;/a&gt; which looked interesting and would do roughly what I wanted.&lt;/p&gt;

&lt;p&gt;This turned out to be a mistake, although initally I seemed to make progress with "simpler" animations (things like pseudo-classes and hover worked great) I reached a roadblock when trying to add the flip animation. The issue I had was translating many combined selctors like, &lt;code&gt;.flip-container.flip .flipper&lt;/code&gt;, into something that worked with J2C.&lt;/p&gt;

&lt;p&gt;I am willing to admit that I simply didn't spend enough time working with J2C so I'm not writing it off completely and it handled some CSS selectors that I never normally get to use with React Native StyleSheets.&lt;/p&gt;

&lt;h3&gt;
  
  
  PostCSS
&lt;/h3&gt;

&lt;p&gt;The next thing to try was plain CSS, I can include a global stylesheet and be done with it. Though I still prefer having style and structure somewhat near each other when building components. The ideal solution would be smaller stylesheets located next to my &lt;code&gt;component.js&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nUhGPIxP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2020-04-26/css-side-by-side.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nUhGPIxP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2020-04-26/css-side-by-side.png" alt="Side-by-Side component and CSS" title="Side-by-side, component and CSS" width="880" height="561"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is made possible by &lt;a href="https://postcss.org"&gt;PostCSS&lt;/a&gt; which, after a bit more webpack wrangling I added to my project. At this point I also setup auto-deployments through zeit.co so while I had spent ~3 hours going down the wrong path I could start to see the app coming together and that was really encouraging!&lt;/p&gt;

&lt;h2&gt;
  
  
  To flip, or not to flip
&lt;/h2&gt;

&lt;p&gt;The first implementation of the flipping was done on hover (&lt;a href="https://blue-lagoon-7fcj4qon3.now.sh"&gt;you can play with this for yourself here&lt;/a&gt;) which was great to test out the behaviour but useless for a game.&lt;/p&gt;

&lt;p&gt;I was following a &lt;a href="https://davidwalsh.name/css-flip"&gt;tutorial&lt;/a&gt; for the CSS flip and there was a section on making it clickable which was achived by toggling a class on the &lt;code&gt;card&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;document.querySelector("#myCard").classList.toggle("flip")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;While toggling the class works it's more imperative than I am used to and I wanted to find a way to do this within the framework as it would be a good chance to explore click-handlers and state.&lt;/p&gt;

&lt;p&gt;After another ~3 hours I was able to cook, eat some dinner and then figure out scope, state and really start to get into my framework of choice.&lt;/p&gt;

&lt;p&gt;I wired everything through and started clicking on cards, only nothing was happening...&lt;/p&gt;

&lt;h3&gt;
  
  
  Baby steps
&lt;/h3&gt;

&lt;p&gt;It turns out I had made a crucial mistake when I first created the Mithril app by calling &lt;code&gt;m.render&lt;/code&gt; instead of &lt;code&gt;m.mount&lt;/code&gt; at the root of my app, from the docs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The m.mount function is similar to m.render, but instead of rendering some HTML only once, it activates Mithril's auto-redrawing system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So while it looked like the app was functioning (because it was rendering) the auto-redrawing system wasn't active which meant any changes to state in the component wouldn't re-render.&lt;/p&gt;

&lt;p&gt;Swapping &lt;code&gt;render&lt;/code&gt; for &lt;code&gt;mount&lt;/code&gt; activated the redraw system solving the issue I had with the &lt;a href="https://blue-lagoon-hl5i0rfq9.now.sh/"&gt;click-flip™️&lt;/a&gt; and that was the final piece of the puzzle, now I could foucs on making something playable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Game on
&lt;/h2&gt;

&lt;p&gt;With that last little &lt;code&gt;mount&lt;/code&gt; / &lt;code&gt;render&lt;/code&gt; hiccup resolved I could tidy up and finish the game logic. The component that was driving the matching / pairing logic was the &lt;code&gt;Stage&lt;/code&gt; and it would be responsible for flipping the cards and counting matches.&lt;/p&gt;

&lt;p&gt;Initially I had thought the &lt;code&gt;Card&lt;/code&gt; would be in control of when it flips (based on click etc) however it ended up coupling the &lt;code&gt;Card&lt;/code&gt; and &lt;code&gt;Stage&lt;/code&gt; too closely and it would simplify the logic greatly if the &lt;code&gt;Card&lt;/code&gt; was a simpler component.&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;Stage&lt;/code&gt; wired up to flip the cards and make a note of all matches it could also determine the end of game, to show the winner a banner, and reset the board.&lt;/p&gt;

&lt;h3&gt;
  
  
  A key play
&lt;/h3&gt;

&lt;p&gt;In React, keys can be used to reset a components state and cause it to re-render and thankfully the same is true with the VNodes of Mithril. When the end of the game is reached I wanted a &lt;code&gt;Play again?&lt;/code&gt; button that could reset the board, shuffle the cards and start the game again.&lt;/p&gt;

&lt;p&gt;I ran into a bit of an unexpected error here. It turns out that if a Mithril component renders multiple children (say a &lt;code&gt;section&lt;/code&gt; with a &lt;code&gt;h2&lt;/code&gt; and a &lt;code&gt;div&lt;/code&gt;) the keys must be all or none - if I want a key on one of the chilren I have to add keys to all of them. The error message for this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Vnodes must either always have keys or never have keys”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What I was trying to do was add a key to one of the entries but not the other. I'm highlighting this because I found the message pretty opaque and there wasn't a lot in the way of results when I searched. In the end I put a breakpoint in the code and stepped through to the component that was breaking it.&lt;/p&gt;

&lt;p&gt;It's always fun to do a bit of debugging through framework source code, you learn loads and it's the best way to understand how it works under the hood; that said good error messages are useful too.&lt;/p&gt;

&lt;p&gt;To finish off I spent a little bit of time writing a helper that converts "matching-pairs" into my card model representation and then thought up a bunch of pairs for testing.&lt;/p&gt;

&lt;h1&gt;
  
  
  Taking stock
&lt;/h1&gt;

&lt;p&gt;I really enjoyed starting a project from scratch, I found the the webpack setup to be a great way to get to grips with a tool I use daily for my job but know little about.&lt;/p&gt;

&lt;p&gt;I like the Mithril framework, I was able to get going with it fairly quickly, aside from a few gotchas, and using the Hyperscript approach (rather than JSX) means it's different enough from React. Mithril still shares enough of the declaritve patterns that I'm used to with React so I don't feel like I'm relearning from scratch.&lt;/p&gt;

&lt;p&gt;Finally animation can be hard, CSS-in-JSS isn't always the right approach but it's always a good idea to take a break and get some food.&lt;/p&gt;

&lt;p&gt;With that I now present to you Blue Lagoon (click to visit) 🎉👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blue-lagoon-in0iqm3fz.now.sh/"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lnOICAgV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://blog.barnettjones.com/public/2020-04-26/blue-lagoon-demo.gif" alt="Blue Lagoon demo" width="503" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading, look out for more in this series to see how this game evolves. Next I'm planning on investigating adding more players into the mix.&lt;/p&gt;

&lt;p&gt;To get updates about this series, you can follow me on Twitter &lt;a href="https://twitter.com/mattjbones"&gt;@mattjones&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S&lt;/strong&gt; This takes time to do, I spent the best part of Saturday building the game and then the bulk of Sunday writing about it - time well spent.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;First appeared on &lt;a href="https://blog.barnettjones.com/2020/04/26/lockdown-game-pt1/"&gt;my blog&lt;/a&gt;, last update 26 Apr 2020&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>mithriljs</category>
      <category>sideprojects</category>
      <category>webpack</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Gaming on macOS, kinda...</title>
      <dc:creator>Matt 🦕</dc:creator>
      <pubDate>Tue, 21 Apr 2020 20:33:28 +0000</pubDate>
      <link>https://dev.to/mattjbones/gaming-on-macos-kinda-1gl5</link>
      <guid>https://dev.to/mattjbones/gaming-on-macos-kinda-1gl5</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Using the power of my gaming computer through the convenience of my MacBook - setup for a headless Windows 10 gaming PC with macOS Steam in-home streaming&lt;/p&gt;

&lt;p&gt;I really like Apple's hardware and enjoy my little, ultraportable, MacBook (&lt;a href="https://www.theverge.com/2019/7/9/20687417/apple-discontinues-12-inch-macbook"&gt;that is sadly discontinued&lt;/a&gt;). One thing Apple hardware falls short on is in the gaming category, that is, unless you punt for the highest model MacBook Pro with a dedicated graphics chip (no thanks), so if I want to play games it's either a PC or PS4. &lt;/p&gt;

&lt;p&gt;In the absence of a desk or monitor, in the past, I have used a &lt;a href="https://store.steampowered.com/steamlink/about"&gt;Steam Link&lt;/a&gt; to play games from my PC on the TV. Steam Link is great for titles that have controller support but less great for RTS's or anything where you have to use a mouse and keyboard. &lt;/p&gt;

&lt;h2&gt;
  
  
  Steam Remote Play
&lt;/h2&gt;

&lt;p&gt;The Steam client, though sometimes lacking in usability, has a bunch of great features - one of which is Remote Play, which allows streaming from one PC to another device. It's Steam Remote Play that powers the Steam Link.  &lt;/p&gt;

&lt;p&gt;Something I never explored was using Steam Remote Play to stream from my gaming PC to my MacBook which should be possible, despite the severe lack of gaming grunt afforded by the ultraportable. &lt;/p&gt;

&lt;h2&gt;
  
  
  Remote Desktop
&lt;/h2&gt;

&lt;p&gt;First thing to do is setup &lt;a href="https://www.groovypost.com/howto/setup-use-remote-desktop-windows-10/"&gt;Remote Deskop&lt;/a&gt; on the gaming PC. The gaming PC runs Windows 10 Pro which can be setup in &lt;code&gt;Settings -&amp;gt; System -&amp;gt; Remote Desktop&lt;/code&gt; and enable.&lt;/p&gt;

&lt;p&gt;Once Remote Desktop is enabled I tested with the free Microsoft Remote Desktop 10 app available from the &lt;a href="https://apps.apple.com/gb/app/microsoft-remote-desktop-10/id1295203466?mt=12"&gt;Mac App Store&lt;/a&gt; (a nice surprise and a really great app). &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Side note:&lt;/em&gt; I have done a bunch of extra work to "secure" the Remote Desktop connection which will come in handy down the line if I want to access from outside my home network. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With Remote Desktop setup I switched to the Mac and used Remote Desktop to do the rest of the configuration (probably not ideal but I don't have a monitor for the PC and staring at the TV gets old, really quickly). &lt;/p&gt;

&lt;h2&gt;
  
  
  Headless Windows
&lt;/h2&gt;

&lt;p&gt;The goal is to have the PC, no monitor attached, boot straight into Steam (big picture mode) and skip loading all the usual Desktop etc. &lt;/p&gt;

&lt;p&gt;In short I want to turn my PC into a games console but with extras.  &lt;/p&gt;

&lt;p&gt;This gaming PC is also used for Plex and a Minecraft Server so there are a handful of other things I want to start when the PC boots. &lt;/p&gt;

&lt;p&gt;Finally, it would be ideal to sleep after a time so some way of waking the PC from sleep without having to press a button would be perfect. &lt;/p&gt;

&lt;p&gt;A, non-exhaustive, checklist of things to solve: &lt;/p&gt;

&lt;p&gt;⬜️ Straight to desktop&lt;br&gt;
⬜️ Booting into Steam (and others)&lt;br&gt;
⬜️ Auto wake &lt;/p&gt;
&lt;h3&gt;
  
  
  Straight to desktop
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This approach is best done on a dedicated non-admin user&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Getting Windows 10 to boot straight to desktop is achieved by changing a registry entry.  The &lt;a href="https://en.wikipedia.org/wiki/Windows_Registry"&gt;Windows Registry&lt;/a&gt; is a huge database of settings for basically everything running on the machine, including Windows itself.  &lt;/p&gt;

&lt;p&gt;Thankfully there are plenty of articles that help when making changes, though tread carefully as it's really easy to bork Windows when fiddling in the Registry.  Following the steps in &lt;a href="https://answers.microsoft.com/en-us/windows/forum/windows_10-desktop/how-to-boot-directly-into-desktop-on-windows-10/9bb80be0-2209-459a-938d-2307dcf183c3"&gt;this article&lt;/a&gt; there are a bunch of values to change in the entry:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;HKEY_LOCAL_MACHINE\Software\Microsoft\WindowsNT\CurrentVersion\Winlogon&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wxfWGTxm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2020-04-21/winlogon-registry-tweaks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wxfWGTxm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2020-04-21/winlogon-registry-tweaks.png" alt="Changing the registry" width="880" height="524"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I still find it nerve-wracking whenever I make changes to the Registry but once these have been applied rebooting my PC take it straight to desktop. &lt;/p&gt;

&lt;p&gt;✅ Straight to desktop&lt;br&gt;
⬜️  Booting into Steam (and others)&lt;br&gt;
⬜️  Auto wake &lt;/p&gt;
&lt;h3&gt;
  
  
  Booting into Steam
&lt;/h3&gt;

&lt;p&gt;There is something called Kiosk mode which does this but it's limited to Windows 10 Pro / Enterprise and seemed to be restricted to UWP Apps - I couldn't see a way to do this with Steam in my cursory search. &lt;/p&gt;

&lt;p&gt;The approach here is to replace the usual Windows 10 Logon shell, Explorer, with something else. Like everything interesting in Windows, this is again configured in the Registry and like the auto login the same entry is needed:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;HKEY_LOCAL_MACHINE\Software\Microsoft\WindowsNT\CurrentVersion\Winlogon&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This time the value to change is "Shell", initially it was &lt;code&gt;explorer.exe&lt;/code&gt; which starts the desktop and kick starts any other loading that happens with it. &lt;/p&gt;

&lt;p&gt;To test this I tried setting it to &lt;code&gt;C:\"Program Files (x86)"\Steam\steam.exe&lt;/code&gt; and rebooting the PC to see, thankfully it booted straight into Steam (I setup Steam to start into Big Picture Mode).  To make sure I could still get back to the safety of my desktop I reverted the change back to &lt;code&gt;explorer.exe&lt;/code&gt; and made sure that was still working. &lt;/p&gt;
&lt;h4&gt;
  
  
  Booting into Steam (and others)
&lt;/h4&gt;

&lt;p&gt;So switching an &lt;code&gt;exe&lt;/code&gt; worked but that is just one program, what about the three that I want to start?&lt;/p&gt;

&lt;p&gt;Operating systems tend to have some way of scripting, that is, adding a bunch of commands into a file and having them execute.  In Windows this can be achieved with a &lt;code&gt;.bat&lt;/code&gt; file or a &lt;a href="https://en.wikipedia.org/wiki/Batch_file"&gt;Batch file&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;The first test was to try starting steam from a batch file. In the weird and wonderful world where Microsoft has added &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10"&gt;Linux to Windows 10&lt;/a&gt; I can start &lt;code&gt;bash&lt;/code&gt; from &lt;code&gt;cmd.exe&lt;/code&gt; and then run &lt;code&gt;vim&lt;/code&gt; to create the batch file... what a time to be alive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ECHO Starting Steam
start C:\"Program Files (x86)"\Steam\steam.exe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's pretty straightforward and easy to test either type the name out in the command prompt or double click. &lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/410357218" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Where I replaced the &lt;code&gt;explorer.exe&lt;/code&gt; with the &lt;code&gt;steam.exe&lt;/code&gt; I can now replace it with the batch file and it will execute each line in the file, which means I can start Minecraft Server and Plex.&lt;/p&gt;

&lt;p&gt;The full startup script looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ECHO Starting Plex
start  C:\"Program Files (x86)"\Plex\"Plex Media Server\Plex Media Server.exe"
ECHO Starting Minecraft
start  D:\Minecraft\start.bat
ECHO Starting Steam
start C:\"Program Files (x86)"\Steam\steam.exe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's important to start both Plex and Minecraft server first so there's not risk they will popup anything over Steam.&lt;/p&gt;

&lt;p&gt;✅ Straight to desktop&lt;br&gt;
✅ Booting into Steam (and others)&lt;br&gt;
⬜ Auto wake &lt;/p&gt;

&lt;h3&gt;
  
  
  Auto wake
&lt;/h3&gt;

&lt;p&gt;The final piece of the puzzle is to enable auto wake, this is part energy saving, part noise reduction and part why not?&lt;/p&gt;

&lt;p&gt;For this there's a cool networking feature called Wake-on-LAN and this allows sleeping computers connected to a network to be woken up by sending a so-called "magic packet". &lt;/p&gt;

&lt;p&gt;Setting this up is done in from Windows &lt;code&gt;Device Manager -&amp;gt; Network Adapters&lt;/code&gt; then finding the Ethernet adapter being used by the computer. In the Advanced tab are two entries &lt;code&gt;Wake on Pattern Match&lt;/code&gt; and &lt;code&gt;Wake on Magic Packet&lt;/code&gt; that needed to be enabled and a checkbox in &lt;code&gt;Power Management&lt;/code&gt; "Allow this device to wake the computer".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--raq534d0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2020-04-21/changes-for-the-magic-packet.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--raq534d0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2020-04-21/changes-for-the-magic-packet.png" alt="Wake on LAN Settings" width="880" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One way to test this is to use a little helper called &lt;code&gt;wakeonlan&lt;/code&gt;, with this&lt;br&gt;
I can broadcast the magic packet to the network and have the PC receive it and&lt;br&gt;
wake. To do this I needed the mac address and the IP address of the subnet mask&lt;br&gt;
both of which I get get (for full details &lt;a href="https://apple.stackexchange.com/a/229596"&gt;StackExchange&lt;/a&gt; had all the answers).&lt;/p&gt;

&lt;p&gt;The other way I tested this was to put the computer to sleep and then use the Plex app on my phone to wake up the PC - this works a treat and with Plex Remote Access I can leave my PC sleeping then wake it up when I need it, wherever I am in the world. &lt;/p&gt;

&lt;p&gt;✅ Straight to desktop&lt;br&gt;
✅ Booting into Steam (and others)&lt;br&gt;
✅ Auto wake &lt;/p&gt;

&lt;h3&gt;
  
  
  Time to play?
&lt;/h3&gt;

&lt;p&gt;After solving everything I needed to solve I started up Steam on my MacBook and then started browsing my games - I fired up &lt;code&gt;Ashes of the Singularity&lt;/code&gt; and was really impressed with how well the splash screen, sound and all, streamed across my home network (wifi + power line adapters). However it didn't look quite right. I couldn't click on any of the UI and it was almost like the screen was too small for the game. &lt;/p&gt;

&lt;p&gt;The final problem to solve was screen resolution of the headless PC. I'm certainly not an expert when it comes to this but from what I know Windows talks to the Graphics Card / display card to work out what resolutions it can display in, 1920x1080, 1280x720, etc. Without a monitor plugged in Windows can't tell what resolution to output and fallback to some defaults. &lt;/p&gt;

&lt;p&gt;Where does Windows store these defaults? Nvidia control panel? Display Settings? Of course not, it's back to the Registry. &lt;/p&gt;

&lt;p&gt;After a bit of googling I found an &lt;a href="https://www.tenforums.com/graphic-cards/10681-tutorial-how-change-windows-10-default-resolution.html"&gt;article that looked promising&lt;/a&gt;. Starting in the entry: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GraphicsDrivers\Configuration&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A few entries for &lt;code&gt;SIMULATED_...&lt;/code&gt; would need to be updated&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sPFeVu3J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2020-04-21/editing-simulated-desktop-resolution.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sPFeVu3J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2020-04-21/editing-simulated-desktop-resolution.png" alt="Editing the default screen resolution in the Registry" width="880" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Within this entry there are a bunch of different configurations, clicking through I found 4 which started with the &lt;code&gt;SIMULATED&lt;/code&gt; prefix and changed each of these to default to 1920x1080. &lt;/p&gt;

&lt;p&gt;One gotcha I ran into was when editing the value, not changing to decimal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tkF8BSc2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2020-04-21/change-to-decimal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tkF8BSc2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2020-04-21/change-to-decimal.png" alt="Editing a value in the Registry" width="738" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the default resolution updated and the PC rebooted I was able to access Steam with a sensible resolution and Stream a game to my MacBook!&lt;/p&gt;

&lt;p&gt;Thanks for reading, I am going to go play some games now, catch you next time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o0Xz-NZg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2020-04-21/ashes-of-the-singularity.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o0Xz-NZg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2020-04-21/ashes-of-the-singularity.png" alt="Ashes of the Singularity played over Steam Remote Play" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;First appeared on &lt;a href="https://blog.barnettjones.com/2020/04/21/macos-gaming/"&gt;my blog&lt;/a&gt;, published 21st April 2020&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>gaming</category>
      <category>macos</category>
      <category>windows</category>
    </item>
    <item>
      <title>Clock</title>
      <dc:creator>Matt 🦕</dc:creator>
      <pubDate>Sun, 03 Nov 2019 23:52:49 +0000</pubDate>
      <link>https://dev.to/mattjbones/clock-1b40</link>
      <guid>https://dev.to/mattjbones/clock-1b40</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Like clocks? I built a clock made of clocks, click below to play with it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.barnettjones.com/clock"&gt;&lt;br&gt;
   &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KDXeJor8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://blog.barnettjones.com/public/2019-11-03/clock-demo.gif" alt="Clock Gif" width="600" height="288"&gt;&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or read on for a more in depth look at building clocks out of clocks (with some React).&lt;/p&gt;
&lt;h2&gt;
  
  
  Inspiration from strange places
&lt;/h2&gt;

&lt;p&gt;Have you something like this before?&lt;/p&gt;


&lt;div class="instagram-position"&gt;
  &lt;iframe id="instagram-liquid-tag" src="https://www.instagram.com/p/BXgLumoBYxg/embed/captioned/"&gt;
  &lt;/iframe&gt;
  
&lt;/div&gt;


&lt;p&gt;Me too, it's cool huh! I really enjoy the way the time seems to gradually appear and take shape.&lt;/p&gt;

&lt;p&gt;Actually, the more I looked at it the more I appreciated the way it was put together, they are not individual clocks in the traditional sense as one arm is not simply following the other. Both arms of the clock are moving freely allowing for the various interesting shapes and movements and, of course, the time.&lt;/p&gt;

&lt;p&gt;I bet that wouldn't be too difficult to put together, a RaspberryPi to control the "clocks" with a mechanism to specify an angle for the first and second hands... &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3o7TKF7iUJ0VgP9Kpi/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3o7TKF7iUJ0VgP9Kpi/giphy.gif" alt="Big Think" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creativity intensifies
&lt;/h2&gt;

&lt;p&gt;Ok, so I don't have the motors to build the physical clocks and I'm not sure what I need to buy or how to wire them into a RaspberryPi. However, what I can do to prototype the idea is build a web app.&lt;/p&gt;

&lt;p&gt;The quickest way to get started is to use &lt;code&gt;create-react-app&lt;/code&gt;, this provides the basic setup for a React app.&lt;/p&gt;

&lt;p&gt;The first thing I wanted to do was to create a simple clock, requirements were simple; two hands that can move independently of each other and a face. Throwing together some &lt;code&gt;div&lt;/code&gt;'s, a bit of CSS and voila, I had the makings of a clock, I can specify the angle in degrees, of each hand using the a CSS transform &lt;code&gt;rotate&lt;/code&gt; and there's a white face. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_Gy0dzqB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2019-11-03/clock-hands.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_Gy0dzqB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2019-11-03/clock-hands.png" alt="An image of the first clock with its arms at odd angles" title="A clock" width="296" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Making things move
&lt;/h2&gt;

&lt;p&gt;From here I wanted to see what the best way to animating things could be. I spent a bit of time looking into the various ways I could animate components in React. Then I thought, nah, why not just see what I can I achieve without a library, surely I should be able to prototype something just using React and a bit of CSS knowhow.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;create-react-app&lt;/code&gt; meant that I get a modern version of React out of the box, which means hooks! I've been looking for an excuse to try out hooks and this seemed a good a time as any to try.&lt;/p&gt;

&lt;p&gt;I imagined a basic rendering loop like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;inital state sets start / end positions&lt;/li&gt;
&lt;li&gt;render clock hands at initial position&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;setTimeout&lt;/code&gt; or &lt;code&gt;requestAnimationFrame&lt;/code&gt; to increment and set new position&lt;/li&gt;
&lt;li&gt;render clock hands at new position&lt;/li&gt;
&lt;li&gt;repeat&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Keeping the hand positions in state meant I could "animate" the hands by updating the state, incrementally, and causing a re-render which would update the hands to their new position.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setAngle&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;getRandomStartingAngle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
   &lt;span class="na"&gt;minute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;getRandomStartingAngle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the new world of React hooks there's a hook perfect for the job of triggering the increment: &lt;code&gt;useEffect&lt;/code&gt; which, amongst other things, is run after every render (for more details check out the docs).&lt;/p&gt;

&lt;p&gt;To increment I needed to create an effect with would trigger at a reasonable rate, for this I used &lt;code&gt;requestAnimationFrame&lt;/code&gt; which is a suitable API to schedule an update on as it's usually called 60 times per second (generally considered the threshold for smooth animation).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;minute&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;setAngle&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
         &lt;span class="na"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hour&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
         &lt;span class="na"&gt;minute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;minute&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Putting it all together and I had a clock that animated around and around, and around, and around, and never stop. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hCvtxKOu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://blog.barnettjones.com/public/2019-11-03/clock-moving.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hCvtxKOu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://blog.barnettjones.com/public/2019-11-03/clock-moving.gif" alt="A gif of the first clock with its arms moving around" title="A clock" width="362" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It seemed to work pretty well. However, I made a couple of mistakes which won't become apparent until I start trying to create numbers from the clocks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drawing numbers, with clocks
&lt;/h2&gt;

&lt;p&gt;Next was to put a bunch of these little clocks onto the screen and see how they animate, using a small bit of &lt;code&gt;flexbox&lt;/code&gt; to define rows / columns and create 2x3 grids for a single number. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GqnuHDUN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2019-11-03/clock-static-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GqnuHDUN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.barnettjones.com/public/2019-11-03/clock-static-2.png" alt="2x3 grid of little clocks with their hands at random positions" title="Can you tell what it is yet?" width="480" height="694"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So it's starting to look a lot more like it could resemble a number. In order to animate to the number I needed to work out all the various positions that could go into a clock number and then tell the smaller clocks to animate to those positions.&lt;/p&gt;

&lt;p&gt;The approach to drawing a number was to pass a &lt;code&gt;targetAngle&lt;/code&gt; to each of the smaller clocks. The basic idea was that for a given target angle the clock would continue increment the position of the hands until they'd reached it, then stop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getIncrementValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetAngle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;targetAngle&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;resetAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Incremental by 1 each time means the target angle would eventually be achieved, however the first mistake I made in the sub clock logic rears its head. &lt;/p&gt;

&lt;p&gt;As the hands increment around they can reach an angle above &lt;code&gt;360deg&lt;/code&gt; which breaks for situations where the hand has to travel around the whole clock to reach the target angle. This would mean some of the sub clocks would stop at the right place but others would continue rotating, an awkward bug.&lt;/p&gt;

&lt;p&gt;To solve the endless rotation bug I added a &lt;code&gt;resetAngle&lt;/code&gt; function which keeps the numbers between &lt;code&gt;0 &amp;lt; 359&lt;/code&gt; allowing the target angle to always be reached.&lt;/p&gt;

&lt;p&gt;Next was the job of actually figuring out these angles. The approach initially was to write, by hand, each angle, for each number, for each clock in the 2x3 grid... I quickly grew tired of this. Instead it's easier to specify a a number of set positions which are the building blocks of the number.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createAngleSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;minute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;minute&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bothLeft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAngleSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;270&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bothRight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAngleSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;270&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bothTop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAngleSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bothBottom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAngleSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;topAndBottom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAngleSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rightAndLeft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAngleSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;topLeftCorner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAngleSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;topRightCorner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAngleSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;270&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bottomLeftCorner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAngleSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;270&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bottomRightCorner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAngleSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emptySpace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createAngleSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;225&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above is the list of all the positions needed to "draw" the numbers 0-9 and they're used in a number config that looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;TWO&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bothRight&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;a2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;topLeftCorner&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;a3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bottomLeftCorner&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;b1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;topRightCorner&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;b2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bottomRightCorner&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;b3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bothLeft&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result of all this work was the realisation of the numbers. The effect was captured almost exactly as I wanted it, with the number appearing out of the randomness of the single clock faces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Blyljh07--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://blog.barnettjones.com/public/2019-11-03/clock-number-2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Blyljh07--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://blog.barnettjones.com/public/2019-11-03/clock-number-2.gif" alt="2x3 grid of little clocks with their hands at random positions moving to the positions of the number 2" title="Can you tell what it is yet? It's a TWO!" width="526" height="696"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The full &lt;a href="https://blog.barnettjones.com/clock#numbergrid"&gt;NumberGrid&lt;/a&gt; is available for preview and illustrates the whole number set that is used in building the clock. &lt;/p&gt;

&lt;h2&gt;
  
  
  Animations, animations, animations
&lt;/h2&gt;

&lt;p&gt;Earlier I mentioned I had made a mistake when creating the mini-clock, did you catch it?&lt;/p&gt;

&lt;p&gt;Well, my first go with &lt;code&gt;useEffect&lt;/code&gt; was more on feeling than careful study of the documentation. As a result when I first attempted to draw the numbers 0-9, at the same time, there was pretty terrible performance.&lt;/p&gt;

&lt;p&gt;Turns out &lt;code&gt;useEffect&lt;/code&gt; is expected to be triggered and then torn down, as a result it's supposed to provide a &lt;code&gt;cleanup&lt;/code&gt; function to do any bits of cleanup that are needed if an in progress effect needs to be cancelled. This caused a subtle issue as everything seems to animate smoothly however it slowed way down as I scaled up from the 1 mini-clock to the 54 that I needed in order to display the full 0-9 numbers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;increment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;minute&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;targetHour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;minute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;targetMinute&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;targetAngle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;setAngle&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
         &lt;span class="na"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;getIncrementValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetHour&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
         &lt;span class="na"&gt;minute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;getIncrementValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetMinute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;cancelAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetAngle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I corrected this within my effect with &lt;code&gt;cancelAnimationFrame&lt;/code&gt; and added a speed value, via &lt;code&gt;useContext&lt;/code&gt; to give me some control over the mini-clock animations (necessary for building a stopwatch).&lt;/p&gt;

&lt;h2&gt;
  
  
  Clock
&lt;/h2&gt;

&lt;p&gt;I now have all the pieces to build the clock. Using the &lt;code&gt;Date&lt;/code&gt; object and updating the target time every time the hours or seconds changed. The &lt;code&gt;DigitalClock&lt;/code&gt; would then work out the individual parts of the time string and pass them to the &lt;code&gt;ClockNumbers&lt;/code&gt; which would, in turn, pass the individual parts to each mini clock.&lt;/p&gt;

&lt;p&gt;I am so happy with the result 🕑🕤🕜😬&lt;/p&gt;

&lt;p&gt;Thanks for reading and check out the clock below 👇&lt;br&gt;
&lt;a href="https://blog.barnettjones.com/clock"&gt;&lt;br&gt;
   &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KDXeJor8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://blog.barnettjones.com/public/2019-11-03/clock-demo.gif" alt="Clock Gif" width="600" height="288"&gt;&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;First appeared on &lt;a href="https://blog.barnettjones.com"&gt;my blog&lt;/a&gt;, published 3rd November 2019&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>sideprojects</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
