<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Joel Auterson</title><link>https://www.joelotter.com/posts/</link><description>Hello! My name's Joel and I'm a software developer. No, not that one.</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Thu, 04 Jun 2026 08:57:35 +0100</lastBuildDate><atom:link href="https://www.joelotter.com/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Plot is the momentum killer, or something</title><link>https://www.joelotter.com/posts/2026/06/saros/</link><pubDate>Thu, 04 Jun 2026 08:57:35 +0100</pubDate><guid>https://www.joelotter.com/posts/2026/06/saros/</guid><description>Some thoughts after 33 hours of Saros.</description><content:encoded>&lt;p&gt;There was a thing about ten years ago in games crit circles (god, maybe it was&#10;more than that, time is an onmarching army etc.) where absolutely everyone&#10;seemed to be obsessed with the idea of &lt;em&gt;ludonarrative dissonance&lt;/em&gt;. This is the&#10;idea that the story in a game, and what you&amp;rsquo;re doing in that game, are&#10;misaligned or even in opposition.&lt;/p&gt;&#10;&lt;p&gt;Saros is a game about a stubborn, stupid man who keeps doing the same thing over&#10;and over again and refusing to learn from it. In that regard, a roguelite is&#10;perfect for it. No notes, no dissonance here, thank you.&lt;/p&gt;&#10;&lt;p&gt;However! The game does have a few places where the story, or more precisely the&#10;format in which it is delivered, gets in the way of what you&amp;rsquo;re doing. Saros&#10;delivers its story through a combination of cutscenes - which are really well&#10;animated and acted, though the dialogue itself is quite wooden - and text and&#10;audio logs, which I thought were very well written.&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s the former that cause problems. Saros really wants to tell you its story in&#10;a set timeframe, and to do that it needs to pull you back to The Passage, its&#10;home base, in order to show you cutscenes.&lt;/p&gt;&#10;&lt;p&gt;This can be really annoying. Getting a good run and a good build in Saros feels&#10;fantastic; regular enemies and sometimes even bosses simply melt before you and&#10;you&amp;rsquo;re excited to push on and see what&amp;rsquo;s around the next corner, bringing all&#10;your hard-earned toys with you. Nope, says the game. You gotta go back to watch&#10;TV for a bit. Then you&amp;rsquo;ve got to start again.&lt;/p&gt;&#10;&lt;p&gt;Of course, this is a modern roguelite, so there are conveniences like being able&#10;to teleport straight to whichever area you want without needing to do a full&#10;run, which is something made quite difficult by the areas&amp;rsquo; branching, non-linear&#10;structure, but it totally saps that feeling of momentum, of rolling downhill, of&#10;getting the payoff for your meticulous build crafting or sheer luck.&lt;/p&gt;&#10;&lt;p&gt;I don&amp;rsquo;t recall other roguelite games having this problem. I &lt;em&gt;believe&lt;/em&gt; it&amp;rsquo;s&#10;possible to escape in Hades on your first run, though I&amp;rsquo;d never be able to do&#10;it, and if memory serves even Housemarque&amp;rsquo;s previous game, the excellent&#10;Returnal, doesn&amp;rsquo;t force you back to the start. Part of the fun in this genre is&#10;starting again once you&amp;rsquo;ve finished it, to see how much your mastery of the&#10;mechanics helps in the absence of all your permanent upgrades.&lt;/p&gt;&#10;&lt;p&gt;Saros and its plot are not at odds, but the way the plot is given to you breaks&#10;the game&amp;rsquo;s progression mechanics in frustrating ways. Maybe that&amp;rsquo;s ludonarrative&#10;dissonance after all. Maybe it&amp;rsquo;s just poor narrative design.&lt;/p&gt;&#10;</content:encoded></item><item><title>Star Wars Outlaws is really good</title><link>https://www.joelotter.com/posts/2026/04/star-wars-outlaws/</link><pubDate>Tue, 21 Apr 2026 19:36:08 +0100</pubDate><guid>https://www.joelotter.com/posts/2026/04/star-wars-outlaws/</guid><description>&lt;p&gt;So I rolled credits on Star Wars Outlaws today and I have come away from it&#10;feeling far more strongly than I ever expected.&lt;/p&gt;</description><content:encoded>&lt;p&gt;So I rolled credits on Star Wars Outlaws today and I have come away from it&#10;feeling far more strongly than I ever expected.&lt;/p&gt;&#10;&lt;p&gt;I think, like a lot of people, I&amp;rsquo;d sort of written off this game at launch. I&amp;rsquo;ve&#10;grown fatigued with open-world games in general, and in particular with&#10;Ubisoft&amp;rsquo;s style of checklist-and-icon driven exploration, and it hadn&amp;rsquo;t done&#10;very well with critics either.&lt;/p&gt;&#10;&lt;p&gt;One unbreakable rule I have with games is &amp;ldquo;if Sam Greer recommends you something&#10;you have to at least try it&amp;rdquo;, and this hasn&amp;rsquo;t steered me wrong yet. Star Wars&#10;Outlaws is great.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2026/04/star-wars-outlaws/outlaws1.jpg" alt="A screenshot of Star Wars Outlaws"&gt;&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s from Ubisoft Massive - the Division folks - and they seem to have really&#10;found a nice balance between giving you a big space to explore and not have the&#10;game drag on forever. Compared to your Ass Creeds and so on Outlaws is &lt;em&gt;svelte&lt;/em&gt;&#10;- I wrapped it up in about 30 hours, and that&amp;rsquo;s with a decent chunk of the side&#10;stuff.&lt;/p&gt;&#10;&lt;p&gt;There&amp;rsquo;s a main story going on - which is really quite good, more on that in a&#10;sec - and simultaneously you&amp;rsquo;re doing all this stuff with the syndicates, who&#10;will ask you to do various jobs in order to gain reputation with them. Some of&#10;these quests involve pinching stuff from the other syndicates, so your&#10;rep goes up and down, and this actually affects what bits of the map you can&#10;walk into freely.&lt;/p&gt;&#10;&lt;p&gt;More interesting is how this actually intersects with the&#10;main story - it&amp;rsquo;s not an isolated side thing. I had a couple occasions where a&#10;main story mission was way harder because I&amp;rsquo;d pissed somebody off, and some&#10;where it was a cakewalk because instead of &amp;ldquo;sneak into this base and grab the&#10;thing&amp;rdquo; it was &amp;ldquo;go in there, say hi to Tony, and grab the thing and anything else&#10;that&amp;rsquo;s not nailed down&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2026/04/star-wars-outlaws/outlaws2.jpg" alt="A screenshot of Star Wars Outlaws with some Jawas"&gt;&lt;/p&gt;&#10;&lt;p&gt;The story is set between Empire and Return, which is the perfect place to put a&#10;story like this. The Empire is still powerful but the cracks are showing, the&#10;Rebellion are around and crossing your path, and in the chaos there&amp;rsquo;s the&#10;perfect opportunity for the best type of story in games, which is a heist.&lt;/p&gt;&#10;&lt;p&gt;Nothing here is particularly groundbreaking even within the medium - &amp;ldquo;we&amp;rsquo;re&#10;assembling a team for a big job&amp;rdquo; is damn near monomyth at this point - but the&#10;way the characters are written, the way their motivations and pasts interact,&#10;all makes for great drama. I found the main character, Kay, to be particularly&#10;well-realised.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m not actually &lt;em&gt;that&lt;/em&gt; big of a Star Wars guy. I think more than any of the&#10;individual films or shows what I love about it is the feel of it and its wide&#10;open plain of storytelling potential. The setting is huge, both in time and&#10;space, and it&amp;rsquo;s got such a particular look to it. That kind of &amp;ldquo;this tech is&#10;very advanced but it&amp;rsquo;s been advanced for so long we treat it like tools in a&#10;garden shed&amp;rdquo;. Love it.&lt;/p&gt;&#10;&lt;p&gt;Outlaws nails this. It looks - and, maybe more importantly, &lt;em&gt;sounds&lt;/em&gt; - like Star&#10;Wars. I also love that they mostly resisted the temptation to have it chock full&#10;of egregious cameos; there are a few, which I won&amp;rsquo;t spoil if you care about&#10;that, but they&amp;rsquo;re all very believable within the context of the story. Glup&#10;Shitto isn&amp;rsquo;t in this one, though, sorry.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2026/04/star-wars-outlaws/outlaws3.jpg" alt="A screenshot of Star Wars Outlaws"&gt;&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s gorgeous, too. I played on Medium settings (I like to play at 4K but my GPU&#10;does not) and even then it was really visually impressive. More importantly, to&#10;me, is that there are &lt;em&gt;so&lt;/em&gt; many bespoke animations, little bits of flair the&#10;developers did not need to put in but did anyway.&lt;/p&gt;&#10;&lt;p&gt;If you try to get off Kay&amp;rsquo;s speeder when it&amp;rsquo;s in an awkward spot she&amp;rsquo;ll jump up&#10;on the seat and step off the&#10;back of it, something I imagine must require some horribly complex inverse&#10;kinematics witchcraft. There&amp;rsquo;s also an extremely long and bespoke animation for&#10;each street food dish you eat with Nix, which is entirely unnecessary, adds very&#10;little and I adore it. The game is full of things like this.&lt;/p&gt;&#10;&lt;p&gt;I think it&amp;rsquo;s a real shame that we&amp;rsquo;re unlikely to get a sequel. Often&#10;when I finish up a big game I&amp;rsquo;m just happy to be finished and move on, but I was&#10;genuinely a bit sad to be leaving these characters behind, and I think that&amp;rsquo;s&#10;neat.&lt;/p&gt;</content:encoded></item><item><title>Giving up on Elden Ring</title><link>https://www.joelotter.com/posts/2026/02/elden-ring/</link><pubDate>Mon, 16 Feb 2026 18:37:46 +0000</pubDate><guid>https://www.joelotter.com/posts/2026/02/elden-ring/</guid><description>The final boss of the Elden Ring DLC has defeated me. I&amp;rsquo;m trying to be at&#10;peace with this.</description><content:encoded>&lt;p&gt;&lt;em&gt;This post contains spoilers for the end of Elden Ring&amp;rsquo;s Shadow of the Erdtree&#10;DLC.&lt;/em&gt;&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve never really thought of myself as a Souls Guy but I guess I am. I&amp;rsquo;ve put&#10;hundreds of hours into these things across Dark Souls, Bloodborne and Elden&#10;Ring. I navigate Lordran and Yarnham in my daydreams.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve been writing for &lt;a href="https://www.joelotter.com/posts/2016/12/top-5-2016/#1-bloodborne-fromsoftware"&gt;literally a decade&lt;/a&gt; now about Bloodborne and&#10;its impact on me, how it changed how I thought about what games were &lt;em&gt;for&lt;/em&gt;, how&#10;I talk about them. I&amp;rsquo;ve spoken to friends about how, look, it&amp;rsquo;s not you against&#10;the game, right, it&amp;rsquo;s you against &lt;em&gt;yourself&lt;/em&gt;, you know? You learn a lot about&#10;your own brain from how you overcome these challenges and stay calm in the face&#10;of frustration. It&amp;rsquo;s meditative, like.&lt;/p&gt;&#10;&lt;p&gt;Shadow of the Erdtree is an excellent DLC. It contains some really wonderfully&#10;designed areas and boss fights. Shadow Keep and the ascent up Jagged Peak are&#10;series highlights. It expands upon the story and setting in interesting ways.&#10;It&amp;rsquo;s hard, but not undoably so. I enjoyed the challenge, right up until the last&#10;boss, and at this point me and myself were pretty united against the game.&lt;/p&gt;&#10;&lt;p&gt;Promised Consort Radahn is, to start with, the archetypal Hard Souls Boss - fast&#10;and complex movements, long combo chains, hits like a truck. It&amp;rsquo;s learnable,&#10;though, and at this point I can get through most of the first phase without&#10;getting hit, usually applying bleed. It&amp;rsquo;s neat.&lt;/p&gt;&#10;&lt;p&gt;Then there&amp;rsquo;s a cutscene, then it&amp;rsquo;s bullshit. My guy moves at the speed of light,&#10;throws fake ghost attacks at you so you don&amp;rsquo;t know when to block or roll, has&#10;area-of-effect moves that seem borderline impossible to dodge short of just&#10;running to the edge of the arena. This is not strictly unique in the pantheon of&#10;Souls bosses, but it&amp;rsquo;s the intensity of it that gets you. I&amp;rsquo;ve managed to drop&#10;him to around a third health before and I&amp;rsquo;ve no idea how I did it; I think I&#10;just got lucky. I can barely land a hit on this lad.&lt;/p&gt;&#10;&lt;p&gt;Lots of my favourite moments in games start out like this: impossible, until,&#10;slowly and then all at once, they aren&amp;rsquo;t. The dash in Hyper Light Drifter, the&#10;Blood Starved Beast in Bloodborne, finally cracking a raid puzzle in Destiny -&#10;these are all incredibly rewarding and justify their own existence with the&#10;benefit of hindsight.&lt;/p&gt;&#10;&lt;p&gt;Radahn starts out impossible and, many hours of attempts later, remains&#10;impossible. I can&amp;rsquo;t do it.&lt;/p&gt;&#10;&lt;p&gt;When I get stuck in a From game I like to look up whatever discussion exists&#10;online about the thing I&amp;rsquo;m stuck on. I tend to play these games slowly, so&#10;there&amp;rsquo;s usually a good amount of chatter by the time I get to it. Often the&#10;bosses are &amp;ldquo;solved&amp;rdquo;, in that there&amp;rsquo;s a sort of known-best technique you can use&#10;if you get stuck. Nothing like this seems to exist for Radahn. Threads about how&#10;to beat him are full of complex builds and loadouts, but there are also lots of&#10;commenters saying, simply, &amp;ldquo;I gave up&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;This is unusual for the Souls community; bloody-minded perserverence is kind of&#10;a religion to these folks. What&amp;rsquo;s doubly unusual in Radahn&amp;rsquo;s case is the number&#10;of responses to these declarations of resignation with, &amp;ldquo;yeah, fair, me too&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m in a Discord chat with some pals who are all very good at these games. They&#10;had encouraged me through the hardest parts of the base game, through Maliketh,&#10;through Malenia, through Commander Gaius. With Radahn, they&amp;rsquo;re encouraging me to&#10;walk away now I&amp;rsquo;ve stopped having fun. &amp;ldquo;You&amp;rsquo;ve already seen everything&amp;rdquo;, Sam&#10;says.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2026/02/elden-ring/radahn.jpg" alt="A screenshot of the cutscene with Radahn"&gt;&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s true, I have seen everything. I haven&amp;rsquo;t beaten every single optional boss,&#10;but I&amp;rsquo;ve done most of it, I&amp;rsquo;ve maxed out my Scadutree whatsit, I&amp;rsquo;ve been to&#10;everywhere on the map. I&amp;rsquo;m not really motivated to beat Radahn, especially&#10;knowing this is the end of the game. There&amp;rsquo;s nothing more to see.&lt;/p&gt;&#10;&lt;p&gt;Ten years ago, at the peak of my Souls obsession, I think I was motivated mostly&#10;by the challenge, the desire - the &lt;em&gt;need&lt;/em&gt; - to prove myself to myself, to overcome&#10;that adversarial relationship with my own brain and push through. I don&amp;rsquo;t think&#10;I have that same drive any more. I&amp;rsquo;m here for a good time. I&amp;rsquo;m not so arrogant&#10;as to say I&amp;rsquo;ve nothing to prove, but I&amp;rsquo;m not going to do it by beating Radahn.&lt;/p&gt;&#10;&lt;p&gt;I feel the need to justify my decision - that&amp;rsquo;s what this post is, I guess - in&#10;part because I feel like I need to explain it to a past version of me. It&amp;rsquo;s also&#10;because I&amp;rsquo;m breaking a rule I set for myself at the start of this year, which&#10;was that I was only going to play one or two games at a time, and I couldn&amp;rsquo;t&#10;pick up a new one until I finished one. I&amp;rsquo;d made space in my head for the&#10;understanding that some games would get abandoned because I didn&amp;rsquo;t want to&#10;finish them, but I hadn&amp;rsquo;t reckoned with the possibility that there would be some&#10;where I &lt;em&gt;couldn&amp;rsquo;t&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;Because you and I are friends, I will admit to you that I am quite a&#10;checklist-driven person. If I was more into film I&amp;rsquo;d be a demon on Letterboxd,&#10;believe me. This rule I&amp;rsquo;ve set for myself this year has genuinely been quite&#10;fun - I engage more with a game if it&amp;rsquo;s the only thing I&amp;rsquo;m playing, and I don&amp;rsquo;t&#10;have to play tedious catchup with the story or mechanics - but pushing through&#10;to beat Radahn just so I could mark it &amp;ldquo;done&amp;rdquo; on my checklist would be a waste&#10;of my limited hours on this earth. My friend William recently &lt;a href="https://www.wiwafrog.online/time-is-not-worth-60/"&gt;wrote about&lt;/a&gt; games that do and don&amp;rsquo;t&#10;respect your time. I think Elden Ring is respectful of my time, but it&amp;rsquo;s on me&#10;to respect my own time too, if that makes sense.&lt;/p&gt;&#10;&lt;p&gt;I also know, due to the way I tend to play games, that giving up now means I&amp;rsquo;ll&#10;probably never come back to this. Maybe that&amp;rsquo;s OK. I might not have finished&#10;Shadow of the Erdtree, but I am finished &lt;em&gt;with&lt;/em&gt; it.&lt;/p&gt;&#10;&lt;p&gt;There isn&amp;rsquo;t even an achievement for beating him anyway.&lt;/p&gt;&#10;</content:encoded></item><item><title>The games of 2025</title><link>https://www.joelotter.com/posts/2025/12/games-played-2025/</link><pubDate>Wed, 31 Dec 2025 18:57:43 +0000</pubDate><guid>https://www.joelotter.com/posts/2025/12/games-played-2025/</guid><description>&lt;p&gt;I do this every year and I need to stop writing &amp;ldquo;hope next year is better for&#10;the industry!&amp;rdquo; because it just never is. I still hope, though.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I do this every year and I need to stop writing &amp;ldquo;hope next year is better for&#10;the industry!&amp;rdquo; because it just never is. I still hope, though.&lt;/p&gt;&#10;&lt;p&gt;I dunno, man. I&amp;rsquo;ve come to write this list with possibly the least energy I&amp;rsquo;ve&#10;ever had for it. I did play some games, and I enjoyed them, but it&amp;rsquo;s&#10;increasingly hard to stem the feeling that this won&amp;rsquo;t, maybe can&amp;rsquo;t, continue, in&#10;several different senses.&lt;/p&gt;&#10;&lt;p&gt;This year brought yet more studio closures, rocketing hardware prices, and the&#10;ever-growing waterfall of money into the toilet that is generative AI. I want to&#10;explore more of the indie and art space but that is being squeezed out of&#10;existence by funding simply not existing any more. BAFTA-award-winning designers&#10;are broke while the same people who said Web 3 was the future stuff chatbots&#10;into every orifice they can reach. I&amp;rsquo;m tired.&lt;/p&gt;&#10;&lt;p&gt;On a more positive note, I enjoy my work, I get to work with some very talented&#10;and nice people, and I shipped a game at the end of the year! Dave&amp;rsquo;s Word Game&#10;is out for iOS and Android and &lt;a href="https://www.playdwg.com"&gt;you should play it&lt;/a&gt;. I&#10;also got married, which isn&amp;rsquo;t about video games but is very good.&lt;/p&gt;&#10;&lt;p&gt;I almost exclusively played games on PC this year, primarily on the Steam Deck,&#10;though I did get the MiSTer unit out for a run in the spring, which was nice.&#10;This year brought the Switch 2, probably the least exciting console launch I can&#10;remember, and while I do like it, I like it in the way I like my new phone&#10;rather than in the way I drew pictures of the Wii on MS Paint when I was 12.&lt;/p&gt;&#10;&lt;p&gt;I played 33 games for the first time this year. As always, if I didn&amp;rsquo;t feel like&#10;I gave it a fair shake, it doesn&amp;rsquo;t count, so while I did play about half an hour&#10;of Blue Prince I don&amp;rsquo;t really feel I have an opinion on it.&lt;/p&gt;&#10;&lt;p&gt;You can see my lists from previous years here:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2024/12/games-played-2024"&gt;2024&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2023/12/games-played-2023"&gt;2023&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2022/12/games-played-2022"&gt;2022&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2021/12/games-played-2021"&gt;2021&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2020/12/games-played-2020"&gt;2020&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2019/12/games-played-2019"&gt;2019&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2018/12/games-played-2018"&gt;2018&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2017/12/games-played-2017"&gt;2017&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="games-i-played-for-the-first-time-in-2025"&gt;Games I played for the first time in 2025&lt;/h2&gt;&#10;&lt;h4 id="the-case-of-the-golden-idol-pc"&gt;The Case of the Golden Idol (PC)&lt;/h4&gt;&#10;&lt;p&gt;I need to start taking notes when I actually play the games. I loved Golden Idol&#10;but I played it right at the start of the year so I don&amp;rsquo;t feel I have much to&#10;say about it other than &amp;ldquo;it&amp;rsquo;s really great&amp;rdquo;. I want to play the sequel.&lt;/p&gt;&#10;&lt;h4 id="eternal-strands-pc"&gt;Eternal Strands (PC)&lt;/h4&gt;&#10;&lt;p&gt;I really liked what I played of Eternal Strands. The setting is interesting, it&#10;feels great in the hands and, while some of the writing is not to my taste, it&amp;rsquo;s&#10;the sort of game in the sort of budget range I think we&amp;rsquo;ve been sorely missing.&#10;More of this, please.&lt;/p&gt;&#10;&lt;h4 id="baldurs-gate-2-pc"&gt;Baldur&amp;rsquo;s Gate 2 (PC)&lt;/h4&gt;&#10;&lt;p&gt;The first Baldur&amp;rsquo;s Gate is, frankly, a chore, albeit a very historically&#10;interesting chore. I found its story to be thin, and it&amp;rsquo;s so dependent on dice&#10;rolls that a combat encounter can be brute forced by just trying it over and&#10;over again.&lt;/p&gt;&#10;&lt;p&gt;Baldur&amp;rsquo;s Gate 2 runs in the same engine, looks the same, mechanically plays the&#10;same (it&amp;rsquo;s still AD&amp;amp;D I think) but is totally transformative. Companions are far&#10;more fleshed out, the stakes are instantly personal in a way that pulls you in&#10;so much quicker, and the world seems that much richer. You could see the&#10;through line from Baldur&amp;rsquo;s Gate to BioWare in their prime, though it was faint.&#10;The line from Baldur&amp;rsquo;s Gate 2 is drawn in big thick marker pen.&lt;/p&gt;&#10;&lt;h4 id="wilmot-works-it-out-pc"&gt;Wilmot Works It Out (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/games-played-2025/wilmot.jpg" alt="A screenshot of Wilmot"&gt;&lt;/p&gt;&#10;&lt;p&gt;I started playing Wilmot with Lucy on Sunday mornings before we went climbing&#10;and it became a really lovely morning ritual. It&amp;rsquo;s so easy to pick it up for&#10;just one puzzle, though rather harder to stop that becoming seven puzzles, and&#10;it works great as a co-op game with one person controlling and the other&#10;helping to spot pieces. I tend to love everything Hollow Ponds puts out so it&#10;wasn&amp;rsquo;t a huge surprise this ended up being one of my favourites this year.&lt;/p&gt;&#10;&lt;h4 id="avowed-xbox"&gt;Avowed (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;It&amp;rsquo;s &amp;ldquo;Obsidian do Elder Scrolls&amp;rdquo;, sure, but I think the thing missing from that&#10;equation is that Obsidian are &lt;em&gt;really good&lt;/em&gt; at doing what Bethesda does but&#10;better, and have been for years. It&amp;rsquo;s a gorgeous game. Sadly I cancelled my&#10;Game Pass this year and didn&amp;rsquo;t get to finish it, but I loved my time with it.&lt;/p&gt;&#10;&lt;h4 id="monster-hunter-wilds-pc"&gt;Monster Hunter Wilds (PC)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;ve tried so many times to get into the Monster Hunter series, but this was it!&#10;This was going to be the one!&lt;/p&gt;&#10;&lt;p&gt;It wasn&amp;rsquo;t. I had fun, but there&amp;rsquo;s a fussiness and a fiddliness to Monster&#10;Hunter that I simply do not get on with, but hugely respect as part of what&#10;makes these games work for the people that like them. Not for me, sadly.&lt;/p&gt;&#10;&lt;h4 id="chrono-trigger-snes"&gt;Chrono Trigger (SNES)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;d been hearing about Chrono Trigger for my entire gaming life but I&amp;rsquo;d never&#10;actually sat down to play it. I&amp;rsquo;m so glad I gave it a shot, it&amp;rsquo;s fantastic. I&#10;haven&amp;rsquo;t finished it yet - playing on MiSTer adds some faff to getting into the&#10;game - but I do mean to go back and do so.&lt;/p&gt;&#10;&lt;h4 id="ys-turbografx-cd"&gt;Ys (Turbografx CD)&lt;/h4&gt;&#10;&lt;p&gt;I was reading online somewhere about how the Ys series has been running for&#10;decades and is still the same storyline and characters. No reboots or complex&#10;sub-series. I&amp;rsquo;d heard good things so I gave it a try.&lt;/p&gt;&#10;&lt;p&gt;Listen: bump combat needs to come back. There&amp;rsquo;s a hit indie game just waiting in&#10;someone&amp;rsquo;s brain to revive it. Please.&lt;/p&gt;&#10;&lt;p&gt;Also: Turbografx games have killer soundtracks.&lt;/p&gt;&#10;&lt;h4 id="promise-mascot-agency-pc"&gt;Promise Mascot Agency (PC)&lt;/h4&gt;&#10;&lt;p&gt;I enjoyed Paradise Killer so was looking forward to Promise Mascot Agency, and&#10;it&amp;rsquo;s really great. The (deliberate) tonal whiplash in places is so very funny. I&#10;did wonder if it would end up feeling a lot like the Yakuza games but it&amp;rsquo;s very&#10;much its own thing (though getting Takaya Kuroda to voice the main character is&#10;a hell of a flex).&lt;/p&gt;&#10;&lt;h4 id="clair-obscur-expedition-33-pc"&gt;Clair Obscur: Expedition 33 (PC)&lt;/h4&gt;&#10;&lt;p&gt;I don&amp;rsquo;t know if I liked E33 &lt;em&gt;as much&lt;/em&gt; as some other people did - I think the&#10;rhythm stuff is a little looser than I&amp;rsquo;d like, and a lot of the environments&#10;don&amp;rsquo;t feel visually cohesive to me - but it&amp;rsquo;s still a fantastic time. The voice&#10;acting and voice direction is a particular highlight; games always have an&#10;issue when trying to get characters to interrupt or talk over one another,&#10;presumably due to separate recording. E33 does this really well.&lt;/p&gt;&#10;&lt;h4 id="skin-deep-pc"&gt;Skin Deep (PC)&lt;/h4&gt;&#10;&lt;p&gt;Another game I played, liked, and didn&amp;rsquo;t return to. Skin Deep does the immersive&#10;sim thing so confidently, and the writing is really sharp. One for next year.&lt;/p&gt;&#10;&lt;h4 id="mario-kart-world-switch-2"&gt;Mario Kart World (Switch 2)&lt;/h4&gt;&#10;&lt;p&gt;It&amp;rsquo;s more Mario Kart. The open world stuff is somewhat interesting at first and&#10;then quickly becomes mundane. The whole package feels a little underbaked. I&#10;don&amp;rsquo;t know, I had fun with it for a few hours and then never touched it again.&lt;/p&gt;&#10;&lt;h4 id="death-stranding-2-on-the-beach-ps5"&gt;Death Stranding 2: On The Beach (PS5)&lt;/h4&gt;&#10;&lt;p&gt;Death Stranding was a surprise favourite for me back at launch, when I played it&#10;on the PS4. The later Director&amp;rsquo;s Cut made some changes, my least favourite of&#10;which was really emphasising the combat. On The Beach unfortunately continues&#10;this trend. I don&amp;rsquo;t &lt;em&gt;want&lt;/em&gt; to shoot guys, I want to sneak around ghosts! Said&#10;ghosts are still here, and there are some scary new ones, but they&amp;rsquo;re all&#10;trivially easy to avoid. Ten or so hours in, the whole game feels trivially&#10;easy. I&amp;rsquo;m hoping this shifts as I get further in - it&amp;rsquo;s so endearingly batshit&#10;I want to keep going.&lt;/p&gt;&#10;&lt;h4 id="destiny-2-edge-of-fate-pc"&gt;Destiny 2: Edge of Fate (PC)&lt;/h4&gt;&#10;&lt;p&gt;Destiny had a weird year in 2024 - The Final Shape was fantastic, but it put a&#10;pretty neat bow on the story, meaning that people were given a fairly neat&#10;opportunity to dip at that point. Bungie needed to pull out something fantastic&#10;to convince people there was life in the old warhorse yet.&lt;/p&gt;&#10;&lt;p&gt;They did not. Edge of Fate has some interesting plot stuff, and I like Lodi a&#10;lot, but it&amp;rsquo;s all buried under a story I&amp;rsquo;ve already forgotten, the most naff&#10;destination so far and, most unfortunately, the enshittification of every other&#10;part of the game.&lt;/p&gt;&#10;&lt;p&gt;Bungie&amp;rsquo;s staff have been through a lot of shit these last few years and I&#10;desperately hope the studio finds its feet soon. I love Destiny and I want it to&#10;continue but, while Renegades would give me a little bit of hope later in the&#10;year, it still looks like it&amp;rsquo;s on its last legs.&lt;/p&gt;&#10;&lt;h4 id="donkey-kong-bananza-switch-2"&gt;Donkey Kong Bananza (Switch 2)&lt;/h4&gt;&#10;&lt;p&gt;I hoped this would be the Super Mario Odyssey of the Switch 2. It isn&amp;rsquo;t, but&#10;it&amp;rsquo;s still really good! It makes fantastic use of the new hardware and I love&#10;how colourful it is, and how much it encourages you to just mess around with the&#10;environments. If this hadn&amp;rsquo;t come out I&amp;rsquo;d be a bit miffed about my Switch 2&#10;purchase.&lt;/p&gt;&#10;&lt;h4 id="frog-detective-3-corruption-at-cowboy-county-pc"&gt;Frog Detective 3: Corruption at Cowboy County (PC)&lt;/h4&gt;&#10;&lt;p&gt;I finally got around to Frog Detective 3 and I didn&amp;rsquo;t even finish it. I don&amp;rsquo;t&#10;know why. I adore the first two, and it&amp;rsquo;s just as funny and charming and well&#10;made. I wonder if I just played it at a time when its sunny, optimistic humour&#10;just wasn&amp;rsquo;t what I needed.&lt;/p&gt;&#10;&lt;h4 id="öoo-pc"&gt;Öoo (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/games-played-2025/ooo.jpg" alt="A screenshot of Öoo"&gt;&lt;/p&gt;&#10;&lt;p&gt;Öoo has a really simple premise - you&amp;rsquo;re a caterpillar made of a couple of&#10;bombs, which you can detonate to propel yourself or interact with the&#10;environment - but the mileage NamaTakahashi gets from this is incredible. Some&#10;of the puzzles are really fiendish; I confess I did have to look up solutions&#10;for a couple. The game doesn&amp;rsquo;t outstay its welcome either so mechanics fatigue&#10;never sets in. A really special game.&lt;/p&gt;&#10;&lt;h4 id="shinobi-art-of-vengeance-pc"&gt;Shinobi: Art of Vengeance (PC)&lt;/h4&gt;&#10;&lt;p&gt;I feel so bad for this game because I feel like in a year without Silksong it&amp;rsquo;d&#10;be all anyone talked about for a month. It&amp;rsquo;s made by Lizardcube, who made one of&#10;my favourite games of the last decade, the Wonder Boy remake, and that sense of&#10;artistry and attention to detail is present here, as well as some incredibly&#10;slick 2D combat. I really hope it&amp;rsquo;s done well enough to keep that studio afloat.&lt;/p&gt;&#10;&lt;h4 id="metal-gear-solid-delta-snake-eater-pc"&gt;Metal Gear Solid Delta: Snake Eater (PC)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;d never played MGS3, so I thought I&amp;rsquo;d give this a look. It&amp;rsquo;s &lt;em&gt;interesting&lt;/em&gt; but&#10;after speaking to a couple friends and reading Joe Wintergreen&amp;rsquo;s &lt;a href="https://www.joewintergreen.com/mgs3-and-photorealisming-the-painterly-game/"&gt;blog post&lt;/a&gt;&#10;about it (shout out to Joe Wintergreen&amp;rsquo;s blog btw it&amp;rsquo;s ace) I think I&amp;rsquo;m going to&#10;go back and play the original, somehow.&lt;/p&gt;&#10;&lt;h4 id="hollow-knight-silksong-pc"&gt;Hollow Knight Silksong (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/games-played-2025/silksong.jpg" alt="A screenshot of Silksong"&gt;&lt;/p&gt;&#10;&lt;p&gt;Much has been written about Silksong&amp;rsquo;s difficulty. I think it &lt;em&gt;is&lt;/em&gt; a cruel game,&#10;and I fully understand why some people I know - people much better at these&#10;sorts of games than I am! - bounced off.&lt;/p&gt;&#10;&lt;p&gt;I went back to Silksong recently after some time away from it, and the point I&amp;rsquo;m&#10;at in the game seems to be when things are opening up for me. This is both in&#10;a literal sense - I&amp;rsquo;ve unlocked many more areas and don&amp;rsquo;t, currently, have a&#10;bottleneck boss to try and beat - but also in terms of my own relationship with&#10;it. Something has clicked, where the mechanics that once felt difficult now&#10;feel, well, still really difficult, but also playful and expressive, maybe even&#10;more so than Hollow Knight.&lt;/p&gt;&#10;&lt;h4 id="final-fantasy-tactics-pc"&gt;Final Fantasy Tactics (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/games-played-2025/fftactics.jpg" alt="A screenshot of Final Fantasy Tactics"&gt;&lt;/p&gt;&#10;&lt;p&gt;Final Fantasy Tactics Advance was a childhood favourite of mine, but I&amp;rsquo;d never&#10;played the original, so this year&amp;rsquo;s remake was a good opportunity. I&amp;rsquo;m playing&#10;the remastered version with the new graphics and the voice acting - I actually&#10;love the pixel art in it, and that strange sort of not-dithered-but-something&#10;look, and the voice acting is really good.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m not far enough in the game yet to have any real strong opinion (other than&#10;&amp;ldquo;oh boy this is quite hard isn&amp;rsquo;t it&amp;rdquo;) but I&amp;rsquo;m really enjoying it so far.&lt;/p&gt;&#10;&lt;h4 id="ball-x-pit-pc"&gt;Ball x Pit (PC)&lt;/h4&gt;&#10;&lt;p&gt;I love Kenny Sun&amp;rsquo;s games - Twinfold is one of the best iOS games ever made - so&#10;I checked this out on launch and it&amp;rsquo;s pretty good. I think I might be over this&#10;style of brain-off Survivors-ish thing now but it&amp;rsquo;s a nicely made one that makes&#10;your brain go &amp;ldquo;ping&amp;rdquo; in that very comforting way.&lt;/p&gt;&#10;&lt;h4 id="strange-antiquities-pc"&gt;Strange Antiquities (PC)&lt;/h4&gt;&#10;&lt;p&gt;I didn&amp;rsquo;t play the previous title, Strange Horticulture, but so far I haven&amp;rsquo;t&#10;felt like I needed to - Strange Antiquities&amp;rsquo; story is very much its own, and&#10;it&amp;rsquo;s a lovely chill time. Play with a mouse, though, the controller support is&#10;fine but clearly not what it&amp;rsquo;s designed for.&lt;/p&gt;&#10;&lt;h4 id="pokemon-black-ds"&gt;Pokemon Black (DS)&lt;/h4&gt;&#10;&lt;p&gt;I skipped this one at the time because my friend said it was bad, and while I&#10;did get to Black 2 a few years later I didn&amp;rsquo;t have a clue what was going on. I&#10;picked up Black this year and it turns out Bill&amp;rsquo;s a liar, it&amp;rsquo;s great. There are&#10;some notable downgrades from the 4th generation, especially how the bottom&#10;screen gets used, but it already feels like it has one of the more compelling&#10;stories in this series. Bar&amp;rsquo;s on the floor, mind, but still.&lt;/p&gt;&#10;&lt;h4 id="anthology-of-the-killer-pc"&gt;Anthology of the Killer (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/games-played-2025/killer.jpg" alt="A screenshot from Anthology of the Killer"&gt;&lt;/p&gt;&#10;&lt;p&gt;Anthology of the Killer is a compilation of short games by ridiculously prolific&#10;creator &lt;a href="http://harmonyzone.org/Videogames.html"&gt;thecatamites&lt;/a&gt;. I heard about it&#10;for the first time on &lt;a href="https://www.sidestory.show/"&gt;Side Story&lt;/a&gt; (my new&#10;favourite video games podcast), played it on a long-haul flight, and it became&#10;my favourite game I played this year.&lt;/p&gt;&#10;&lt;p&gt;To attempt to describe it would be to do it a disservice, I think, because the&#10;writing in these games is &lt;em&gt;just so good&lt;/em&gt;, so I won&amp;rsquo;t try. I also won&amp;rsquo;t spoil any&#10;of the many, many wonderful jokes and scenes. Instead I&amp;rsquo;ll just beg you to play&#10;it. Go play it.&lt;/p&gt;&#10;&lt;h4 id="stray-children-pc"&gt;Stray Children (PC)&lt;/h4&gt;&#10;&lt;p&gt;Onion Games&amp;rsquo; output has always fascinated me and Stray Children continues their&#10;streak of really interesting games. I understand it to be a sort of spiritual&#10;successor to Moon, an RPG I haven&amp;rsquo;t yet played, but so far I&amp;rsquo;m really enjoying&#10;it even if I&amp;rsquo;m struggling to &lt;em&gt;entirely&lt;/em&gt; follow what&amp;rsquo;s going on.&lt;/p&gt;&#10;&lt;h4 id="demonschool-pc"&gt;Demonschool (PC)&lt;/h4&gt;&#10;&lt;p&gt;I love Persona and I love tactics games and Demonschool is on its face a lovely&#10;fusion of the two. And it is lovely, but it&amp;rsquo;s also very slow to start, a bit&#10;repetitive in the encounter design and not always super clearly tutorialised. If&#10;you go in prepared for that you&amp;rsquo;ll have a great time, I think.&lt;/p&gt;&#10;&lt;h4 id="absolum-pc"&gt;Absolum (PC)&lt;/h4&gt;&#10;&lt;p&gt;A beat-&amp;rsquo;em-up! In 2025! Rejoice! Absolum is great. I&amp;rsquo;ve not played enough yet to&#10;dig deep into the roguelite stuff but it feels wonderful to play and the voice&#10;acting is a treat. Dominique Tipper! Amazing.&lt;/p&gt;&#10;&lt;h4 id="winter-burrow-pc"&gt;Winter Burrow (PC)&lt;/h4&gt;&#10;&lt;p&gt;I didn&amp;rsquo;t actually play this one, I watched my wife play it, so for the first&#10;time ever on this blog, we have a guest writer. Award-winning journalist Lucy&#10;White:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;It&amp;rsquo;s cute.&amp;rdquo;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Thank you, Lucy.&lt;/p&gt;&#10;&lt;p&gt;The thing about a pure survival game - I mean one where you gather resources to&#10;get more resources, turn the wheel to be able to turn the bigger wheel, none of&#10;the creative stuff like Minecraft or Subnautica - is that the aesthetics are&#10;kind of &lt;em&gt;all there is&lt;/em&gt;. You&amp;rsquo;re doing the same thing, so how does it make you&#10;feel turning the wheel?&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&amp;ldquo;I feel emotionally attached to the mouse.&amp;rdquo;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;Winter Burrow&amp;rsquo;s neat.&lt;/p&gt;&#10;&lt;h4 id="destiny-2-renegades-pc"&gt;Destiny 2: Renegades (PC)&lt;/h4&gt;&#10;&lt;p&gt;It&amp;rsquo;s a lot better than Edge of Fate, but from my time with it it feels more like&#10;a biggish season rather than a full expansion. In a way that&amp;rsquo;s fine - I&amp;rsquo;d&#10;been missing seasons - but I doubt it&amp;rsquo;s going to win back any lapsed players.&lt;/p&gt;&#10;&lt;p&gt;The Star Wars thing was kind of a turn-off for me before launch - I find the&#10;Fortniteification of all games to be frankly galling - but it&amp;rsquo;s actually done&#10;very tastefully, which was a really pleasant surprise. I&amp;rsquo;d say if you&amp;rsquo;ve already&#10;paid for it as part of the bundle then definitely do play it. Otherwise? Err um&#10;oh there&amp;rsquo;s the doorbell-&lt;/p&gt;&#10;&lt;h4 id="sektori-pc"&gt;Sektori (PC)&lt;/h4&gt;&#10;&lt;p&gt;I love twin stick shooters and I&amp;rsquo;m very bad at them. Sektori is great if you&#10;love twin stick shooters, and it makes me want to be less bad at them. I don&amp;rsquo;t&#10;know if that will happen, but a boy can dream.&lt;/p&gt;&#10;&lt;h4 id="star-wars-outlaws-pc"&gt;Star Wars Outlaws (PC)&lt;/h4&gt;&#10;&lt;p&gt;I was gifted this by good talented pal Sam Greer as a prize for winning a quiz,&#10;and while I&amp;rsquo;ve not been able to play it for a few weeks due to it not running on&#10;Steam Deck, in the first hour I was pretty instantly charmed by it. I like Kay a&#10;lot more than any of the various unmemorable protagonists from modern Star Wars&#10;(Andor excepted) and moving between stealth and combat feels surprisingly fluid.&#10;It&amp;rsquo;s gorgeous, too. The Snowdrop engine continues to impress, and I&amp;rsquo;d be really&#10;interested to know why Ubisoft have so many bloody engines.&lt;/p&gt;&#10;&lt;h4 id="lorelei-and-the-laser-eyes-pc"&gt;Lorelei and the Laser Eyes (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/games-played-2025/lorelei.jpg" alt="A screenshot from Lorelei"&gt;&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve not yet played more than about half an hour of Blue Prince, but I do feel&#10;like a lot of what people liked about it might also apply to Lorelei, and I&#10;wonder why it didn&amp;rsquo;t get quite the same level of adulation. Ask me again when&#10;I&amp;rsquo;ve played Blue Prince, I guess.&lt;/p&gt;&#10;&lt;p&gt;Anyway: Lorelei is amazing. The puzzle difficulty is pitched perfectly, and&#10;while I always feel at least a little bit bewildered there&amp;rsquo;s always some thread&#10;I can be pulling on, somewhere. I&amp;rsquo;m at about 50% completion and I&amp;rsquo;ve had a few&#10;genuine lightbulb moments (and more than a few &amp;ldquo;can&amp;rsquo;t believe I missed that&amp;rdquo;&#10;moments) which feel great. I&amp;rsquo;ve played it a lot over the Christmas break and&#10;it&amp;rsquo;s one of my favourite games of the year despite being from, y&amp;rsquo;know, last&#10;year.&lt;/p&gt;&#10;&lt;h2 id="some-other-games-i-finished-in-2025"&gt;Some other games I finished in 2025&lt;/h2&gt;&#10;&lt;h4 id="baldurs-gate-pc"&gt;Baldur&amp;rsquo;s Gate (PC)&lt;/h4&gt;&#10;&lt;p&gt;I reread what I wrote about this in &lt;a href="https://www.joelotter.com/posts/2023/12/games-played-2023/#baldurs-gate-pc"&gt;2023&lt;/a&gt; and, yeah, I stand by all of&#10;that. It&amp;rsquo;s really interesting, but I don&amp;rsquo;t know if I&amp;rsquo;d recommend it to anyone.&lt;/p&gt;&#10;&lt;h4 id="disco-elysium-pc"&gt;Disco Elysium (PC)&lt;/h4&gt;&#10;&lt;p&gt;I finally finished it, it&amp;rsquo;s brilliant, it&amp;rsquo;s the end of the list and I&amp;rsquo;m tired,&#10;you already know about Disco.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;&lt;em&gt;Did I miss something? Angry at me for some other reason? Yell at me on &lt;a href="https://bsky.app/profile/joelotter.com/post/3mbfhzah2fs2m"&gt;Bluesky&lt;/a&gt; or &lt;a href="https://otter.garden/@joel/115822307274862466"&gt;Mastodon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>Things I learned making a game engine</title><link>https://www.joelotter.com/posts/2025/12/growl/</link><pubDate>Sun, 14 Dec 2025 12:50:19 +0000</pubDate><guid>https://www.joelotter.com/posts/2025/12/growl/</guid><description>I&amp;rsquo;ve spent the last few years building my own game engine from scratch.&#10;Here are some of the things I learned.</description><content:encoded>&lt;p&gt;Our next game, &lt;a href="https://www.playdwg.com"&gt;Dave&amp;rsquo;s Word Game&lt;/a&gt;, comes out tomorrow.&#10;This is very exciting for several reasons, but for me personally&#10;it&amp;rsquo;s significant as it&amp;rsquo;s the first thing we&amp;rsquo;ve done in Growl, my game engine&#10;project I&amp;rsquo;ve been working on, at first sporadically and then pretty seriously,&#10;for the last few years.&lt;/p&gt;&#10;&lt;p&gt;I started doing this mostly for fun and for my own learning, but I had a couple&#10;of other motivations. The first was that our previous game,&#10;&lt;a href="https://store.steampowered.com/app/863400/Feud/"&gt;Feud&lt;/a&gt;, was written in Java&#10;using libGDX. libGDX is a great library, but, while I&amp;rsquo;m not a Java hater, it&#10;certainly caused us no end of issues around things like code signing and&#10;portability to other platforms, particularly iOS. Compound that with my dream of&#10;eventually releasing stuff on Switch and it became clear I needed something&#10;else.&lt;/p&gt;&#10;&lt;p&gt;Around 2019, I did a little bit of open source work on&#10;&lt;a href="https://github.com/amzeratul/halley/"&gt;Halley&lt;/a&gt;, the game engine used by&#10;Chucklefish and largely developed by Rodrigo Braz Monteiro, a fearsomely&#10;talented game programmer. Rodrigo was kind and patient enough to answer all my&#10;stupid questions and I eventually managed to port the engine to iOS and to&#10;Metal, Apple&amp;rsquo;s graphics library. I had so much fun. I was hooked.&lt;/p&gt;&#10;&lt;p&gt;It was at around this time that I started entertaining the idea of making my own&#10;engine. The amount of stuff I&amp;rsquo;d need to learn was intimidating - I had basically&#10;zero experience in graphics programming and none whatsoever in fonts, audio,&#10;input, UI and all the myriad other things needed to make even the simplest 2D&#10;engine function. A few years later, in early 2022, I took my first baby steps.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m not going to try and make the argument here that anyone &lt;em&gt;should&lt;/em&gt; make their&#10;own game engine. If your priority is to actually ship a game, it&amp;rsquo;s probably the&#10;most time-inefficient way possible to make that happen. I&amp;rsquo;ve seen so many&#10;friends fall into the trap of having so much fun making the engine they never&#10;get around to making a game in it, and while I actually think this is a pretty&#10;nice outcome it&amp;rsquo;s not what I wanted for myself. However, I can&amp;rsquo;t think of any&#10;other software project I&amp;rsquo;ve ever done where I have pushed myself so far, learned&#10;so much, and spoken to so many interesting and talented people in the process.&#10;If you want to have a good time writing code, a game engine might be the best&#10;way to do that.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m going to write this chronologically, going through the various different&#10;things I had to learn about and implement to get this working. The timeline will&#10;be a bit strange - I had to finish Feud while I was doing this, and then at a&#10;certain point the majority of development was on Dave&amp;rsquo;s Word Game itself and not&#10;the engine. Feel free to follow along in &lt;a href="https://github.com/Bearwaves/growl/commits/main/"&gt;Growl&amp;rsquo;s commit history&lt;/a&gt;, if you dare.&lt;/p&gt;&#10;&lt;p&gt;This post is quite long. Here&amp;rsquo;s a table of contents.&lt;/p&gt;&#10;&lt;div class="toc"&gt;&#10; &lt;nav id="TableOfContents"&gt;&#10; &lt;ul&gt;&#10; &lt;li&gt;&lt;a href="#jan-mar-2022-engine-structure-metal-opengl"&gt;Jan-Mar 2022: Engine structure, Metal, OpenGL&lt;/a&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;a href="#apr-jun-2022-input-fonts-and-audio"&gt;Apr-Jun 2022: Input, fonts and audio&lt;/a&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;a href="#jul-sep-2022-imgui-ios-and-the-beginnings-of-daves-word-game"&gt;Jul-Sep 2022: ImGui, iOS and the beginnings of Dave&amp;rsquo;s Word Game&lt;/a&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;a href="#oct-dec-2022-android-silly-porting-and-scene-graph"&gt;Oct-Dec 2022: Android, silly porting, and scene graph&lt;/a&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;a href="#2023-i-did-other-things"&gt;2023: I did other things&lt;/a&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;a href="#jan-jul-2024-scripting-and-shaders"&gt;Jan-Jul 2024: Scripting and shaders&lt;/a&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;a href="#aug-dec-2024-ui-and-starting-dwg-in-earnest"&gt;Aug-Dec 2024: UI and starting DWG in earnest&lt;/a&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;a href="#2025-haptics-networking-misc"&gt;2025: Haptics, networking, misc.&lt;/a&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;a href="#conclusions-acknowledgements-and-whats-next"&gt;Conclusions, acknowledgements, and what&amp;rsquo;s next&lt;/a&gt;&lt;/li&gt;&#10; &lt;/ul&gt;&#10;&lt;/nav&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;Let&amp;rsquo;s go.&lt;/p&gt;&#10;&lt;h2 id="jan-mar-2022-engine-structure-metal-opengl"&gt;Jan-Mar 2022: Engine structure, Metal, OpenGL&lt;/h2&gt;&#10;&lt;p&gt;I decided at the beginning I was going to use C++ 17; my reasons for this were&#10;probably laughably vague, being simply &amp;ldquo;that&amp;rsquo;s what Halley uses&amp;rdquo;. In hindsight,&#10;there have been a few C++ 20 features I&amp;rsquo;ve missed out on but largely 17 has been&#10;fine. At the time, though, I barely knew C++, and had some stupid idea that I&#10;was going to try and write the engine in such a way that someone could use it&#10;without knowing all that &amp;ldquo;hard stuff&amp;rdquo; like move semantics. I was quickly&#10;disabused of this notion by my good pals Pat and Stu, yet more extremely&#10;talented programmers with a seemingly inexhaustible supply of patience for Local&#10;Idiot Asking Questions. This is one of the prerequisites of writing a game&#10;engine.&lt;/p&gt;&#10;&lt;p&gt;(Stu&amp;rsquo;s new game, &lt;a href="https://store.steampowered.com/app/3012600/Substructure/"&gt;Substructure&lt;/a&gt;, was revealed&#10;recently, and you should definitely check if out if you&amp;rsquo;re a Factorio sicko like&#10;he is.)&lt;/p&gt;&#10;&lt;p&gt;I knew just about enough about CMake at this point to hurt myself badly with it.&#10;I had to do quite a substantial refactor later in the year.&lt;/p&gt;&#10;&lt;p&gt;The structure of the engine was heavily inspired by Halley, which uses a plugin&#10;based architecture where everything is a type of plugin; system, graphics,&#10;audio, network and so on. The API was designed to be very much like libGDX, with&#10;an &amp;ldquo;outside-in&amp;rdquo; style of architecture where the user is responsible for bringing&#10;the executable entry point. I had to bend this a bit for iOS and Android.&lt;/p&gt;&#10;&lt;p&gt;I started out building the basic interfaces for the various APIs, which at the&#10;beginning looked like this:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;SystemAPI&lt;/code&gt; is responsible for input, window management if applicable,&#10;logging, interfacing with the host OS and doing things like checking for dark&#10;mode or running haptics.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;GraphicsAPI&lt;/code&gt; is for all things drawing. Shaders, sprite batches, textures,&#10;all that good stuff.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;AudioAPI&lt;/code&gt; is for audio, duh.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;I would eventually add &lt;code&gt;NetworkAPI&lt;/code&gt; and &lt;code&gt;ScriptingAPI&lt;/code&gt;, but that wasn&amp;rsquo;t til&#10;quite a bit later.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/window.jpg" alt="A screenshot showing a macOS window with a red background."&gt;&lt;/p&gt;&#10;&lt;p&gt;My first System API was for SDL2, and for graphics it was my old friend Metal.&#10;The first thing I ever got it to do was create a window and clear the background&#10;colour in it. I was extremely proud of this.&lt;/p&gt;&#10;&lt;p&gt;The next thing was loading and rendering textures. Metal makes this relatively&#10;easy to do, but I still needed to go and relearn what the hell a projection was,&#10;having not touched this kind of maths since university. Here&amp;rsquo;s an early attempt&#10;using some assets from Feud - you can see I hadn&amp;rsquo;t really got my head around&#10;the matrix maths yet.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/textures.jpg" alt="A screenshot showing some stretched out images on a red background."&gt;&lt;/p&gt;&#10;&lt;p&gt;Eventually I got a somewhat sensible structure working for textures, sprite&#10;batches, and shaders. I then started trying to implement the same thing in&#10;OpenGL as an additional graphics plugin.&lt;/p&gt;&#10;&lt;p&gt;I think I may be somewhat atypical in going from Metal to OpenGL rather than the&#10;other way around, and while this did make some things quite hard I think it also&#10;taught me some good habits - putting things in buffers, for example, is&#10;the default in Metal, and while it&amp;rsquo;s considered a modern best practice in OpenGL&#10;a lot of the beginner tutorials don&amp;rsquo;t teach it.&lt;/p&gt;&#10;&lt;p&gt;I started using this image of a cat for testing as it was something I could put&#10;into a public repo, and it stayed as the unofficial mascot of Growl until I&#10;eventually &lt;a href="https://www.joelotter.com/notes/2023/12/27-growl/"&gt;replaced it&lt;/a&gt;&#10;with a picture of my own cat.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/opengl.jpg" alt="A screenshot showing two cats rendered in a window."&gt;&lt;/p&gt;&#10;&lt;p&gt;Next, I had my eye on doing texture atlases, something I used extensively in&#10;Feud, and for that I&amp;rsquo;d need the ability to create those offline. I was and am&#10;resistant to the idea of shipping the engine with an &amp;ldquo;editor&amp;rdquo; - I tend to do&#10;everything I possibly can in a terminal and resent ever needing to touch a&#10;mouse - so I made a command-line tool, &lt;code&gt;growl-cmd&lt;/code&gt;. I used &lt;a href="https://github.com/CLIUtils/CLI11"&gt;CLI11&lt;/a&gt; for this, which is great. This tool has&#10;basically been unchanged since; I had a vision of also adding stuff like Metal&#10;shader precompilation to it but never really got around to it.&lt;/p&gt;&#10;&lt;p&gt;The texture atlases and render-to-texture came next. This was fun! I learned a&#10;lot writing my own bleed algorithm for the texture atlases and implementing the&#10;skyline algorithm for the packing, and needing to grapple with concepts like&#10;half-pixel correction was a tremendous learning experience even if it did make&#10;my brain hot like an old laptop. Some of the test images from the time were&#10;trippy, I wish I still had them.&lt;/p&gt;&#10;&lt;p&gt;One fairly important thing I added at this point was a Result/Error type. I&#10;don&amp;rsquo;t really like the way exceptions work in most languages, but I do like Go&amp;rsquo;s&#10;style of value based error handling, so I made a simple class for this, based on&#10;&lt;code&gt;std::variant&lt;/code&gt;. It was at this stage I needed to go and really get my head&#10;around move semantics, rvalues, and a small amount of templates; things I had&#10;little comparable experience in from other languages, so this was hard. The&#10;&lt;code&gt;#joel-learns-things&lt;/code&gt; channel on my Discord server got a lot of use this month.&lt;/p&gt;&#10;&lt;h2 id="apr-jun-2022-input-fonts-and-audio"&gt;Apr-Jun 2022: Input, fonts and audio&lt;/h2&gt;&#10;&lt;p&gt;I added keyboard, mouse and gamepad input using SDL&amp;rsquo;s event system. I remember&#10;being surprised at how easy it was - SDL does a huge amount for you here. Having&#10;even the most basic input possible suddenly changed this from feeling like a&#10;rendering library to feeling like something that could make &lt;em&gt;games&lt;/em&gt;. Steady on,&#10;kid, long way to go yet.&lt;/p&gt;&#10;&lt;p&gt;Because I didn&amp;rsquo;t at this stage have a particular game I was trying to make, I&#10;was free to follow my own interests. I didn&amp;rsquo;t have any sort of audio support in&#10;the engine yet but I had a couple of weeks off work and decided to take a big&#10;ol&amp;rsquo; detour into text rendering.&lt;/p&gt;&#10;&lt;p&gt;Text is a fascinating topic that more than deserves its own writeup, which I&amp;rsquo;ve&#10;been telling myself I&amp;rsquo;ll do for three years now. Due to my frustrations getting&#10;certain languages supported in Feud, I had pretty big ambitions for this; I was&#10;going to support CJK, RTL, BiDi, emojis, the lot. In the end I added Signed&#10;Distance Fields to this list. I&amp;rsquo;m pretty proud of Growl&amp;rsquo;s text rendering.&lt;/p&gt;&#10;&lt;p&gt;The first bit was learning to actually parse fonts and extract the glyphs, so&#10;that I could pack them into a texture atlas offline. Feud would rebuild font&#10;atlases on the fly, which works totally fine for English and is abysmally slow&#10;for Simplified Chinese. Here&amp;rsquo;s an image showing the extracted glyphs of an emoji&#10;font, for some reason:&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/emojis.jpg" alt="A grid of emojis rendered on a fake transparent background."&gt;&lt;/p&gt;&#10;&lt;p&gt;With font atlases done came rendering the text. I used a combination of&#10;Harfbuzz, FreeType and utfcpp to parse strings, determine their layout and then&#10;match the glyphs up to those in my font atlas. Harfbuzz does a lot of the work&#10;for you, so extending this to support non-Latin scripts and even emojis with&#10;Zero Width Joiners (used to specify things like skin tone) wasn&amp;rsquo;t too much of a&#10;stretch.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/arabic.jpg" alt="A screenshot showing some arabic text"&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/emoji2.jpg" alt="A screenshot showing some emojis"&gt;&lt;/p&gt;&#10;&lt;p&gt;Next up was the SDF fonts. The big advantage of these is that you can pre-bake&#10;just one font atlas at a fixed size, and then use your shader to make that font&#10;scale and appear crisp at any other size, within limits. This meant I could do&#10;the sort of dynamic text resizing that would have been much harder in Feud, and&#10;I wouldn&amp;rsquo;t have to build fonts at runtime.&lt;/p&gt;&#10;&lt;p&gt;I settled on &lt;a href="https://github.com/Chlumsky/msdfgen"&gt;Multi-Channel Signed Distance Fonts&lt;/a&gt; for this, a method developed by Viktor&#10;Chlumský, inspired by the original Valve paper. This involves generating font&#10;atlases using the three colour channels in addition to the alpha channel SDF&#10;fonts use.&lt;/p&gt;&#10;&lt;p&gt;Integrating this into my rendering stack proved to be quite tricky; I had to do&#10;a lot of fiddling with it to get FreeType&amp;rsquo;s metrics to match up to the metrics&#10;emitted by MSDFgen. I&amp;rsquo;m still making adjustments to it even now, and the MSDF&#10;fonts don&amp;rsquo;t look &lt;em&gt;exactly&lt;/em&gt; like their rasterised counterparts, but it&amp;rsquo;s a&#10;difference so slight only I will notice and get annoyed by it. Here are a few&#10;old screenshots showing my attempts to get it working.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/msdf1.jpg" alt="Hello Growl in hideous colours"&gt;&#10;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/msdf2.jpg" alt="Hello Growl in white but mangled"&gt;&#10;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/msdf3.jpg" alt="Hello Growl in white, less mangled"&gt;&#10;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/msdf4.jpg" alt="Hello Growl, somewhat good looking"&gt;&lt;/p&gt;&#10;&lt;p&gt;I implemented line breaking after this, using &lt;a href="https://github.com/adah1972/libunibreak"&gt;libunibreak&lt;/a&gt;. In the end I didn&amp;rsquo;t get around to&#10;doing bidirectional text, but it&amp;rsquo;s on my list. Here&amp;rsquo;s a screenshot showing where&#10;the text rendering ended up:&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/texts.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I finally got around to doing audio next. I went with SoLoud, which does most of&#10;the work for me, so in the end I didn&amp;rsquo;t need to learn that much about audio&#10;programming. However, SoLoud is now effectively unmaintained, so I have a vague&#10;plan to migrate to MiniAudio, which &lt;em&gt;will&lt;/em&gt; require me to learn a bit more as&#10;that library is rather more low-level.&lt;/p&gt;&#10;&lt;p&gt;The one slightly tricky bit with the audio was getting streaming to work from my&#10;asset bundle - I don&amp;rsquo;t encrypt the data in the bundle but it&amp;rsquo;s all smooshed&#10;together contiguously, so I needed to write some file adapter code to get SoLoud&#10;to be able to read it.&lt;/p&gt;&#10;&lt;h2 id="jul-sep-2022-imgui-ios-and-the-beginnings-of-daves-word-game"&gt;Jul-Sep 2022: ImGui, iOS and the beginnings of Dave&amp;rsquo;s Word Game&lt;/h2&gt;&#10;&lt;p&gt;One thing I regret not investing more in when developing Feud was tooling. Any&#10;change to the game, even tweaking an asset or changing the speed of an&#10;animation, required the other lads to upload things to Google Drive and then get&#10;me to make the change in the code. While I wasn&amp;rsquo;t interested in building an&#10;editor for Growl, I did want some sort of way to make it easier to plug in&#10;external tooling.&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://github.com/ocornut/imgui"&gt;Dear ImGui&lt;/a&gt; was the natural choice for this.&#10;I added a little debug menu in-game, and then certain variables could be bound&#10;to sliders in that UI. I would go on to add quite a bit of tooling to this menu,&#10;eventually turning it into something a bit more like an embedded editor, though&#10;still much leaner than a real editor would be.&lt;/p&gt;&#10;&lt;p&gt;I also started doing the porting process here. iOS was the first port of call,&#10;as I was a bit more familiar with it, though in the intervening few years I&amp;rsquo;d&#10;forgotten how strange Objective C&amp;rsquo;s memory model can be. The plugin architecture&#10;lent itself well to porting, and Metal is consistent enough between macOS and&#10;iOS that I hardly needed to change anything at all, just adjust some memory&#10;alignment issues. I was very pleased when I got the test app running on my iPad;&#10;it brought back that thrill from the Halley days. Porting is so much fun.&lt;/p&gt;&#10;&lt;p&gt;It was around this time that Dave, knowing I was after some kind of simple game&#10;idea to dogfood the engine with, told me about his word game idea, then unnamed.&#10;From this point on DWG became the guiding force of the engine - I&amp;rsquo;d work on it&#10;for a little bit, realise some other huge thing was totally missing, then go and&#10;implement that into Growl. Having this structure meant I was much less likely&#10;to go off on a side quest (though that certainly did still happen). Here&amp;rsquo;s the&#10;earliest screenshot I can find of Dave&amp;rsquo;s Word Game with its original art style,&#10;rendered in Growl.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/dwg1.jpg" alt="A very early screenshot of Dave’s Word Game"&gt;&lt;/p&gt;&#10;&lt;h2 id="oct-dec-2022-android-silly-porting-and-scene-graph"&gt;Oct-Dec 2022: Android, silly porting, and scene graph&lt;/h2&gt;&#10;&lt;p&gt;Android was the next target. I&amp;rsquo;d done quite a lot of Android development - it&#10;was probably the first thing I ever really programmed for - but I&amp;rsquo;d never gone&#10;near the NDK. I ended up using GameActivity, which makes a few things like&#10;input handling a little easier.&lt;/p&gt;&#10;&lt;p&gt;I found Android really interesting. There were a few issues I needed to iron&#10;out - in particular, I only supported OpenGL, not OpenGL ES, so I needed to do a&#10;bit of messing with loaders and so on - but for the most part it was relatively&#10;smooth. The documentation is decent, and I remain pretty amazed at Android&amp;rsquo;s&#10;commitment to backward compatibility; both my test devices were from 2013 and&#10;they worked totally fine.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/android.jpg" alt="A Nexus 7 and HTC One M8 running Growl"&gt;&lt;/p&gt;&#10;&lt;p&gt;Amusingly, I could also run it on the Oculus Quest, it being essentially an&#10;Android phone strapped to the face.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/oculus.jpeg" alt="An Oculus Quest screenshot of Growl"&gt;&lt;/p&gt;&#10;&lt;p&gt;I was determined to do everything I could to stay in the NDK and avoid having&#10;any Java code or build system at all; I really didn&amp;rsquo;t want to have to mix CMake&#10;and Gradle, and having multiple languages felt icky. I wrote a &lt;a href="https://www.joelotter.com/posts/2022/10/android-ndk-without-gradle/"&gt;blog post&lt;/a&gt; at the time explaining how to&#10;package an APK without using any of the Gradle based tools. Later, I would&#10;abandon this, as some things simply can&amp;rsquo;t be done without bridging to Java via&#10;JNI.&lt;/p&gt;&#10;&lt;p&gt;I would later come to encounter a lot of Android-specific issues, particularly&#10;around streaming audio from disk, but at this point I was high on the joy of&#10;porting and I was happy.&lt;/p&gt;&#10;&lt;p&gt;Things got a little silly from here out.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/3ds.jpg" alt="A 3DS running Growl, what the hell"&gt;&lt;/p&gt;&#10;&lt;p&gt;I had been very deliberately architecting the engine towards maximum&#10;portability, and getting it to run on 3DS (via the homebrew SDK) felt like the&#10;best possible test of that. It worked, kind of! I never got around to doing&#10;audio, but it was able to load stuff from the asset bundle and render it on&#10;screen.&lt;/p&gt;&#10;&lt;p&gt;3DS is a tricky platform for Growl, which is built around the assumption that&#10;whatever it&amp;rsquo;s running on has a shader pipeline of some description. The 3DS has&#10;programmable vertex shaders but, weirdly, fixed-function fragment shaders. This&#10;meant that getting stuff like the SDF font rendering to work was a little&#10;beyond my capabilities. Still, a good learning experience, and I actually made a&#10;small contribution to Harfbuzz to get it &lt;a href="https://github.com/harfbuzz/harfbuzz/pull/3851"&gt;building on arm-none-eabi&lt;/a&gt;; getting to interact briefly&#10;with Behdad Esfahbod left me a little starstruck.&lt;/p&gt;&#10;&lt;p&gt;I also got the engine running on web, via Emscripten - because Emscripten ships&#10;with an SDL2 implementation this ended up being very easy indeed. You can have a&#10;play with it &lt;a href="https://www.joelotter.com/growl-test-app/"&gt;here&lt;/a&gt;, though bear in&#10;mind it&amp;rsquo;s a very old version of the engine!&lt;/p&gt;&#10;&lt;p&gt;Back to DWG, and realised the next thing I needed was a scene graph or some sort&#10;of UI toolkit; I found that I was essentially implementing that in the game and&#10;thought it made more sense to have in the engine. I put together a simple&#10;hierarchical scene graph, where positions, scales and rotations could be put on&#10;a parent node and they&amp;rsquo;d be applied to any children. I had to root around in my&#10;brain to remember how order of operation for this type of matrix maths works,&#10;but it wasn&amp;rsquo;t too complicated.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/scene.jpg" alt="A screenshot showing some cats in a grid, rotated"&gt;&lt;/p&gt;&#10;&lt;h2 id="2023-i-did-other-things"&gt;2023: I did other things&lt;/h2&gt;&#10;&lt;p&gt;I took a break from Growl at the start of 2023, and this ended up being a break&#10;that lasted nearly the whole year. This surprises me, looking back! I don&amp;rsquo;t&#10;remember a period where I wasn&amp;rsquo;t working on it. Time is an illusion and all&#10;that.&lt;/p&gt;&#10;&lt;p&gt;Anyway, in 2023 I had a couple of other projects consuming all my time. I&#10;had to get Feud out of Early Access, and there was quite a lot to do there. One&#10;of the tasks was to get it built for the Epic Games Store, and making it work&#10;with Epic Online Services. EOS don&amp;rsquo;t ship a Java SDK, and Feud is written in&#10;Java, so I ended up being inspired by &lt;a href="https://github.com/code-disaster/steamworks4j"&gt;steamworks4j&lt;/a&gt; and made my own library, &lt;a href="https://github.com/Bearwaves/eos4j"&gt;eos4j&lt;/a&gt;. This was my first experience using JNI, and&#10;it would come to be very useful later down the line when I had to move a bunch&#10;of Growl&amp;rsquo;s Android stuff out to Java.&lt;/p&gt;&#10;&lt;p&gt;You should play Feud, by the way. It consumed eight years of my life and I have&#10;a sort of resentment towards it but I&amp;rsquo;m also really proud of it.&lt;/p&gt;&#10;&lt;p&gt;Towards the end of 2023 I dipped my toes back into Growl, hooking up the ImGui&#10;debug menu to the scene graph. This meant I could have various widgets and&#10;doodads attached to the nodes in the graph, making it organised a bit more like&#10;what you&amp;rsquo;d see in the editor of a big game engine.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/scene2.jpg" alt="Screenshot of Growl showing cats and debug UI"&gt;&lt;/p&gt;&#10;&lt;p&gt;In the new year, I got a bit distracted again.&lt;/p&gt;&#10;&lt;h2 id="jan-jul-2024-scripting-and-shaders"&gt;Jan-Jul 2024: Scripting and shaders&lt;/h2&gt;&#10;&lt;p&gt;I had this idea that having built-in scripting support would be really useful in&#10;some games. I had a little toy shmup idea I was playing around with and being&#10;able to adjust enemy behaviour and so on without needing to rebuild the game&#10;sounded really cool. We ended up not using this at all in DWG, but it&amp;rsquo;s in the&#10;engine, it works, and I spent ages on it so I&amp;rsquo;m going to talk about it.&lt;/p&gt;&#10;&lt;p&gt;Rather than pick one language as the default, I wanted to make scripting&#10;languages act like plugins via a Scripting API. The idea was you could bind&#10;functions to scripts and then the scripting layer in the engine would take care&#10;of doing all the language-specific bits to connect everything up. I would&#10;provide Lua scripting as a reference implementation, but wanted it to be&#10;possible to drop in something like &lt;a href="https://wren.io/"&gt;Wren&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;This is insanely over-engineered, and it took about six months of my life to get&#10;it properly working. In the process I had to take my first hesitant steps into&#10;the world of template metaprogramming, from which I have still not&#10;psychologically recovered.&lt;/p&gt;&#10;&lt;p&gt;The end result is this fairly clean setup where you can attach a script like&#10;this to any arbitrary node and &amp;ldquo;override&amp;rdquo; any methods that have been made&#10;available to the scripting layer.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-lua" data-lang="lua"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;function&lt;/span&gt; &lt;span class="nc"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;setX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Node.setX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;end&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;return&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;ve not revisited the scripting stuff in over a year and still haven&amp;rsquo;t really&#10;used it in anger, so no doubt there&amp;rsquo;s still a lot to do there.&lt;/p&gt;&#10;&lt;p&gt;Towards the middle of the year I also put a bit of time into tidying up how&#10;Growl manages shaders, applies uniforms and allows for users to package custom&#10;shaders in the asset bundle, then edit them live in the debug menu. Once again,&#10;this wasn&amp;rsquo;t something I needed for DWG, but I&amp;rsquo;d been playing around with&#10;&lt;a href="https://www.shadertoy.com/"&gt;Shadertoy&lt;/a&gt; and thought it&amp;rsquo;d be neat to have a&#10;little shader playground in Growl.&lt;/p&gt;&#10;&lt;p&gt;I went with a setup where every shader gets a shared &amp;ldquo;header&amp;rdquo; prepended to it&#10;before compilation, so that every (fragment) shader has access to variables like&#10;time and screen resolution. I made my little playground and started using it&#10;when doing shader tutorials. It&amp;rsquo;s on &lt;a href="https://github.com/Bearwaves/growl-shader-playground"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/shaders.jpg" alt="A screenshot of Growl shader playground"&gt;&lt;/p&gt;&#10;&lt;h2 id="aug-dec-2024-ui-and-starting-dwg-in-earnest"&gt;Aug-Dec 2024: UI and starting DWG in earnest&lt;/h2&gt;&#10;&lt;p&gt;I had a scene graph, now I needed to add the UI framework on top of that.&#10;I decided to go with something fairly similar to GTK, the first UI library I&#10;ever used - widgets (a specialisation of the scene graph&amp;rsquo;s nodes) get added as&#10;children and provide their packing preferences (expand, fill, margins and so&#10;on).&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2025/12/growl/ui.jpg" alt="A screenshot showing some coloured boxes arranged in a grid"&gt;&lt;/p&gt;&#10;&lt;p&gt;I followed Scene2d&amp;rsquo;s approach to validation/invalidation/layout, which worked&#10;nicely at first and then became quite tricky due to text reflow causing&#10;invalidations up and down the tree. I had to tinker with that for quite a while&#10;to get it working reliably.&lt;/p&gt;&#10;&lt;p&gt;Towards the end of the year, once the UI stuff was in, I felt it was time to go&#10;back to DWG armed with the new Growl features and Dave&amp;rsquo;s newest designs. We&#10;built a prototype relatively quickly, and had our families playing with it over&#10;the Christmas holidays.&lt;/p&gt;&#10;&lt;p&gt;DWG had always been intended as a small dogfooding exercise, but once we started&#10;playing it and saw our families interact with it we realised it was actually&#10;really fun, and after some encouragement we decided to try and make the whole&#10;thing.&lt;/p&gt;&#10;&lt;h2 id="2025-haptics-networking-misc"&gt;2025: Haptics, networking, misc.&lt;/h2&gt;&#10;&lt;p&gt;The focus now squarely on DWG, everything I did on Growl was driven by whatever&#10;that game needed at the time. This meant the development was quite scattered,&#10;mostly small additions and changes rather than big new features, though I did&#10;add a couple of significant things.&lt;/p&gt;&#10;&lt;p&gt;One really fun addition was haptic feedback - in Growl this is responsible for&#10;both mobile haptics and controller rumble. Building this out was really&#10;enjoyable, and the addition of haptics into the game made it immediately feel so&#10;much more polished. iOS in particular has a really robust and powerful haptics&#10;system that you can do a lot with. It&amp;rsquo;s hard to show it in a screenshot, though.&lt;/p&gt;&#10;&lt;p&gt;The other big addition was networking support. This was a lot harder than I&#10;expected; networking is one area I have a lot of confidence in, but the&#10;difficulty ended up being around managing dependencies for it. The various&#10;different platforms handle networking and particularly TLS in quite different&#10;ways, with some having libraries provided and some requiring you to bring your&#10;own. I use vendored dependencies in Growl, but OpenSSL is so large that adding&#10;it immediately ballooned the size of the build. It took a while to work out&#10;which libraries to use on which platform.&lt;/p&gt;&#10;&lt;p&gt;The rest of the year was spent adding what I needed, when I needed it: dark mode&#10;querying, text input, new UI widgets, file I/O, interpolations, a preferences&#10;system and mobile share sheets all made appearances. Most of my time was spent&#10;in the DWG codebase itself, though, so Growl changes became a bit more sporadic.&lt;/p&gt;&#10;&lt;p&gt;I implemented things like platform login and achievements in DWG rather than in&#10;Growl. I don&amp;rsquo;t really have a super good justification for why this stuff felt&#10;like it didn&amp;rsquo;t belong in the engine. Some of the flows do end up being quite&#10;game-specific, and it&amp;rsquo;s quite hard to make achievements stuff generic, but&#10;I might well move it in or make it a separate library at some point.&lt;/p&gt;&#10;&lt;p&gt;Tomorrow, December 15th, Dave&amp;rsquo;s Word Game launches on iOS and Android. We plan&#10;to put the PC version out a little later. Please play it! You can preregister at&#10;&lt;a href="https://www.playdwg.com"&gt;playdwg.com&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h2 id="conclusions-acknowledgements-and-whats-next"&gt;Conclusions, acknowledgements, and what&amp;rsquo;s next&lt;/h2&gt;&#10;&lt;p&gt;In the grand scheme of games and game engines, Growl is very basic. It&amp;rsquo;s 2D only&#10;and doesn&amp;rsquo;t have many of the more advanced features of an engine like Unity or&#10;Godot. If the goal was simply to build Dave&amp;rsquo;s Word Game, it probably would have&#10;been faster to just do it in Java again.&lt;/p&gt;&#10;&lt;p&gt;But! If I hadn&amp;rsquo;t started making my own engine, DWG would probably not have&#10;happened. I wouldn&amp;rsquo;t have learned C++, or OpenGL, or Metal, or about font&#10;rendering, or how to integrate Lua, or CMake, or the myriad other little bits&#10;of knowledge that are now lodged in my brain. It has been intensely rewarding,&#10;and probably the most enjoyable project I&amp;rsquo;ve ever undertaken.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve also met some really interesting and inspiring people. In particular are&#10;the denizens of the Game Engine Developers Discord server, which I was added to&#10;at a time when I couldn&amp;rsquo;t reasonably call myself that. They have been incredibly&#10;supportive, and I want to shout out Ruby, Rodrigo and Pat in particular for&#10;answering my stupid questions, giving encouragement, and saying &amp;ldquo;hell yeah&amp;rdquo; at&#10;exactly the right moments. Also Ruby for the phrase &amp;ldquo;solve problems that exist&amp;rdquo;&#10;which, yeah, I&amp;rsquo;ll keep trying.&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s hard to say right now what the next things to add to Growl are. We have a&#10;roadmap for DWG, but it&amp;rsquo;s mostly things that won&amp;rsquo;t need additions to the engine.&#10;Inevitably, some of them &lt;em&gt;will&lt;/em&gt; turn out to need changes, but I can&amp;rsquo;t yet&#10;foresee any big new features. Perhaps whatever the next game is will drive those&#10;requirements!&lt;/p&gt;&#10;&lt;p&gt;At the back of my mind I do have the desire to tidy up Growl, document it, make&#10;it usable to other people. That&amp;rsquo;s a lot of work, though, and it&amp;rsquo;s not yet clear&#10;there&amp;rsquo;s anyone out there who &lt;em&gt;wants&lt;/em&gt; to use it who wouldn&amp;rsquo;t be better served by&#10;something like Godot, so I&amp;rsquo;m holding fire.&lt;/p&gt;&#10;&lt;p&gt;If you&amp;rsquo;re thinking about building a game engine, be aware that it will consume&#10;so much more of your life than you expect. But you&amp;rsquo;ll have the best time.&lt;/p&gt;&#10;</content:encoded></item><item><title>My favourite command-line tools</title><link>https://www.joelotter.com/posts/2025/02/command-line-tools/</link><pubDate>Wed, 12 Feb 2025 10:16:21 +0000</pubDate><guid>https://www.joelotter.com/posts/2025/02/command-line-tools/</guid><description>&lt;p&gt;A friend got a MacBook recently and was asking for recommendations. I found&#10;myself wishing I had a list of all my default-installed command-line tools, so&#10;that&amp;rsquo;s what this is.&lt;/p&gt;</description><content:encoded>&lt;p&gt;A friend got a MacBook recently and was asking for recommendations. I found&#10;myself wishing I had a list of all my default-installed command-line tools, so&#10;that&amp;rsquo;s what this is.&lt;/p&gt;&#10;&lt;p&gt;These are the programs and tools I install on every machine capable of running&#10;them. I install them with Homebrew on macOS, Pacman on Arch Linux, and,&#10;surprisingly, many of them are available via WinGet if you&amp;rsquo;re brave enough to&#10;try using a terminal on Windows.&lt;/p&gt;&#10;&lt;p&gt;All my personal configurations for these various tools are available for you to&#10;peruse in my &lt;a href="https://github.com/JoelOtter/dotfiles"&gt;dotfiles repo&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Before anyone shouts at me, I&amp;rsquo;m making the choice not to include various&#10;staple tools in a terminal environment, so while I use tools like &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;dig&lt;/code&gt;&#10;and &lt;code&gt;ns&lt;/code&gt; highly regularly I think anyone who needs those already knows about&#10;them. This is more of a &amp;ldquo;hey here&amp;rsquo;s a cool thing to install&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;On we go.&lt;/p&gt;&#10;&lt;h2 id="tmux"&gt;&lt;code&gt;tmux&lt;/code&gt;&lt;/h2&gt;&#10;&lt;a href="https://www.joelotter.com/posts/2025/02/command-line-tools/"&gt;&#10; &lt;img src="tmux.png" alt="A screenshot of tmux running in iTerm2" title="A screenshot of tmux running in iTerm2" /&gt;&#10;&lt;/a&gt;&#10;&#10;&lt;p&gt;&lt;code&gt;tmux&lt;/code&gt; (short for &amp;ldquo;terminal multiplexer&amp;rdquo;) is the first thing I install. At its&#10;most basic, it lets you display multiple terminals in a single window of your&#10;terminal emulator of choice, but it can do a lot more. You can have multiple&#10;windows and even sessions that you can detach and reattach as you please, making&#10;it extremely useful for keeping terminal states around; super handy if you find&#10;yourself SSHing into servers a lot. There&amp;rsquo;s also an increasingly rich plugin&#10;ecosystem, which is what&amp;rsquo;s supplying those CPU/memory metrics in the&#10;bottom-right.&lt;/p&gt;&#10;&lt;h2 id="zsh"&gt;&lt;code&gt;zsh&lt;/code&gt;&lt;/h2&gt;&#10;&lt;p&gt;The choice of shell is a very subjective decision and I&amp;rsquo;m not going to try and&#10;convince you that &lt;code&gt;zsh&lt;/code&gt; is better than &lt;code&gt;fish&lt;/code&gt; or any other shell, simply because&#10;I haven&amp;rsquo;t used the others enough. I settled on &lt;code&gt;zsh&lt;/code&gt; many years ago and I love&#10;its autocompletion, syntax highlighting and rich plugin ecosystem.&lt;/p&gt;&#10;&lt;p&gt;I use &lt;a href="https://github.com/zsh-users/antigen"&gt;antigen&lt;/a&gt; for managing &lt;code&gt;zsh&lt;/code&gt;&#10;plugins; there are newer tools available but I&amp;rsquo;ve never bothered to look at&#10;them because antigen works well for me.&lt;/p&gt;&#10;&lt;p&gt;There&amp;rsquo;s a huge plugin bundle called &lt;code&gt;oh-my-zsh&lt;/code&gt; that you can install, which is&#10;an everything-and-the-kitchen-sink approach to &lt;code&gt;zsh&lt;/code&gt; that I don&amp;rsquo;t actually like&#10;very much - I think long-term you&amp;rsquo;ll have a better time if you start from&#10;scratch and just install the things you want as you go. Vanilla &lt;code&gt;zsh&lt;/code&gt; can do a&#10;lot by itself, it&amp;rsquo;s worth a go and might already be installed on your system.&lt;/p&gt;&#10;&lt;h2 id="bat"&gt;&lt;code&gt;bat&lt;/code&gt;&lt;/h2&gt;&#10;&lt;a href="https://www.joelotter.com/posts/2025/02/command-line-tools/"&gt;&#10; &lt;img src="bat.png" alt="A screenshot of bat displaying a Python file from Bridgy" title="A screenshot of bat displaying a Python file from Bridgy" /&gt;&#10;&lt;/a&gt;&#10;&#10;&lt;p&gt;You may already be familiar with &lt;code&gt;cat&lt;/code&gt;, which is used for printing the contents&#10;of a file to the terminal or piping it into other programs. &lt;code&gt;bat&lt;/code&gt; is,&#10;essentially, &lt;code&gt;cat&lt;/code&gt; with syntax highlighting, along with some other conveniences&#10;like automatically displaying the file in a pager if it&amp;rsquo;s too big for your&#10;terminal. I use it every day.&lt;/p&gt;&#10;&lt;h2 id="eza"&gt;&lt;code&gt;eza&lt;/code&gt;&lt;/h2&gt;&#10;&lt;a href="https://www.joelotter.com/posts/2025/02/command-line-tools/"&gt;&#10; &lt;img src="eza.png" alt="A screenshot of eza showing the files in cool-club webring" title="A screenshot of eza showing the files in cool-club webring" /&gt;&#10;&lt;/a&gt;&#10;&#10;&lt;p&gt;&lt;code&gt;eza&lt;/code&gt; (a fork of the now-unmaintained &lt;code&gt;exa&lt;/code&gt;) is to &lt;code&gt;ls&lt;/code&gt; as &lt;code&gt;bat&lt;/code&gt; is to &lt;code&gt;cat&lt;/code&gt;. It&#10;offers better colour support but also a pile of extra features like showing Git&#10;statuses and has a built-in tree display. I have &lt;code&gt;ls&lt;/code&gt; aliased to &lt;code&gt;eza&lt;/code&gt; now and&#10;have done for years.&lt;/p&gt;&#10;&lt;h2 id="fzf"&gt;&lt;code&gt;fzf&lt;/code&gt;&lt;/h2&gt;&#10;&lt;a href="https://www.joelotter.com/posts/2025/02/command-line-tools/"&gt;&#10; &lt;img src="fzf.png" alt="A screenshot of fzf in the cool-club repo" title="A screenshot of fzf in the cool-club repo" /&gt;&#10;&lt;/a&gt;&#10;&#10;&lt;p&gt;&lt;code&gt;fzf&lt;/code&gt; is a fuzzy-finder. You run it, start typing, and you&amp;rsquo;ll get a list of&#10;matching files along with a little preview. You can pipe the result out to&#10;whatever you like, and it&amp;rsquo;s got really nice integrations with Vim and various&#10;shells.&lt;/p&gt;&#10;&lt;h2 id="git-branch-i"&gt;&lt;code&gt;git-branch-i&lt;/code&gt;&lt;/h2&gt;&#10;&lt;a href="https://www.joelotter.com/posts/2025/02/command-line-tools/"&gt;&#10; &lt;img src="git-branch-i.png" alt="A screenshot of git-branch-i showing two branches in a repo" title="A screenshot of git-branch-i showing two branches in a repo" /&gt;&#10;&lt;/a&gt;&#10;&#10;&lt;p&gt;This is a bit of a cheeky one. &lt;code&gt;git-branch-i&lt;/code&gt; is a &lt;a href="https://github.com/JoelOtter/git-branch-i"&gt;tool I made&lt;/a&gt;, which lets you&#10;switch between or delete Git branches in a more interactive fashion. I use it&#10;every day, along with its sister projects &lt;a href="https://github.com/JoelOtter/k8s-context-i"&gt;&lt;code&gt;kube-context-i&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/JoelOtter/terraform-workspace-i"&gt;&lt;code&gt;terraform-workspace-i&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h2 id="jq"&gt;&lt;code&gt;jq&lt;/code&gt;&lt;/h2&gt;&#10;&lt;a href="https://www.joelotter.com/posts/2025/02/command-line-tools/"&gt;&#10; &lt;img src="jq.png" alt="A screenshot showing a simple selector in jq" title="A screenshot showing a simple selector in jq" /&gt;&#10;&lt;/a&gt;&#10;&#10;&lt;p&gt;&lt;code&gt;jq&lt;/code&gt; (and its YAML equivalent &lt;code&gt;yq&lt;/code&gt;) is an extremely flexible and powerful tool&#10;for working with JSON files. I use it a lot in both scripts and interactively.&lt;/p&gt;&#10;&lt;h2 id="neovim"&gt;&lt;code&gt;neovim&lt;/code&gt;&lt;/h2&gt;&#10;&lt;a href="https://www.joelotter.com/posts/2025/02/command-line-tools/"&gt;&#10; &lt;img src="neovim.png" alt="A screenshot of Neovim" title="A screenshot of Neovim" /&gt;&#10;&lt;/a&gt;&#10;&#10;&lt;p&gt;This is another one that ought to be its own post, really, and I don&amp;rsquo;t want to&#10;start a holy war in here, so I will simply say that Neovim is my single&#10;favourite piece of software that I use.&lt;/p&gt;&#10;&lt;p&gt;I will also add a similar warning to &lt;code&gt;oh-my-zsh&lt;/code&gt; here: Neovim is infinitely&#10;customisable, and with that flexibility comes an intimidating depth. It can seem&#10;really hard to get from the default setup to something that matches the IDE of&#10;your dreams, and to that end there are various really cool projects like&#10;&lt;a href="http://www.lazyvim.org/"&gt;LazyVim&lt;/a&gt; which do this for you. However, I think&#10;you&amp;rsquo;ll be better off in the long run by starting from scratch and finding and&#10;configuring the plugins that work best for you. That way you&amp;rsquo;ll know every bit&#10;of your setup and it will work exactly how you like it, and you&amp;rsquo;ll learn a huge&#10;amount in the process.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;That&amp;rsquo;s all of them for now! I confess I&amp;rsquo;m actually surprised this list is as&#10;short as it is given that I essentially live in the terminal given the choice.&lt;/p&gt;&#10;&lt;p&gt;Have any suggestions? Comment on &lt;a href="https://bsky.app/profile/joelotter.com/post/3lhy44lyanc2y"&gt;Bluesky&lt;/a&gt;, &lt;a href="https://otter.garden/@joel/113990759539360708"&gt;Mastodon&lt;/a&gt; or &lt;a href="https://news.ycombinator.com/item?id=43024437"&gt;Hacker News&lt;/a&gt; and I&amp;rsquo;ll add them to this list!&lt;/p&gt;</content:encoded></item><item><title>The games of 2024</title><link>https://www.joelotter.com/posts/2024/12/games-played-2024/</link><pubDate>Tue, 31 Dec 2024 13:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2024/12/games-played-2024/</guid><description>&lt;p&gt;I wrote this time last year that I hoped 2024 was a better year for the&#10;games industry - this has turned out not to be the case.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I wrote this time last year that I hoped 2024 was a better year for the&#10;games industry - this has turned out not to be the case.&lt;/p&gt;&#10;&lt;p&gt;In a fun inversion of my 2022 list, 2024 was the year I formally &lt;em&gt;rejoined&lt;/em&gt; the&#10;games industry. I&amp;rsquo;m working in codevelopment, which is rather more sheltered&#10;from the storms that have wracked the wider industry, but I&amp;rsquo;ve seen so many&#10;friends lose their work I&amp;rsquo;m in danger of becoming numb to it. Something &lt;em&gt;has&lt;/em&gt; to&#10;shift, and soon, or many of the sort of games that appear on this list would&#10;simply not be made.&lt;/p&gt;&#10;&lt;p&gt;In 2024 we finally released&#10;&lt;a href="https://store.steampowered.com/app/863400/Feud/"&gt;Feud&lt;/a&gt; out of Early Access. The&#10;response was, frankly, pretty muted, but I&amp;rsquo;m glad to have it done after eight&#10;years. We&amp;rsquo;re also deep into development on our next game, which is a different&#10;sort of genre to Feud but still in the &amp;ldquo;board game that works surprisingly well&#10;as a video game&amp;rdquo; space we enjoy playing in. More on that soon.&lt;/p&gt;&#10;&lt;p&gt;Some loose thoughts on my game-playing habits this year:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Handhelds are king. I played a huge amount on the Steam Deck, Analogue Pocket&#10;and even the 3DS got a good amount of play this year.&lt;/li&gt;&#10;&lt;li&gt;We moved house and our new place has Ethernet, so I got Moonlight set up and I&#10;play a lot of games over a local stream now. It&amp;rsquo;s pretty good but I feel like&#10;there&amp;rsquo;s still not a perfect way to interact with it.&lt;/li&gt;&#10;&lt;li&gt;The above means I increasingly play on PC more than any other platform,&#10;particularly after a CPU upgrade thanks to a Japan trip and the weak Yen.&lt;/li&gt;&#10;&lt;li&gt;I should just buy Balatro instead of continuing to pay for Apple Arcade.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;In 2024, I played 38 games for the first time. I wasn&amp;rsquo;t terribly adventurous&#10;this year - you&amp;rsquo;ll likely recognise most of the games on this list - but there&#10;were a few surprises nonetheless. As always, this list doesn&amp;rsquo;t include anything&#10;I bounced off immediately or only played a tiny bit of. I&amp;rsquo;ve included a section&#10;at the end for games from previous years which I &lt;em&gt;finished&lt;/em&gt; this year, which is&#10;an embarrassingly small section.&lt;/p&gt;&#10;&lt;p&gt;You can see my lists from previous years here:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2023/12/games-played-2023"&gt;2023&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2022/12/games-played-2022"&gt;2022&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2021/12/games-played-2021"&gt;2021&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2020/12/games-played-2020"&gt;2020&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2019/12/games-played-2019"&gt;2019&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2018/12/games-played-2018"&gt;2018&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2017/12/games-played-2017"&gt;2017&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="games-i-played-for-the-first-time-in-2024"&gt;Games I played for the first time in 2024&lt;/h2&gt;&#10;&lt;h4 id="marvels-midnight-suns-pc"&gt;Marvel&amp;rsquo;s Midnight Suns (PC)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;d been very excited to get to this one and its inclusion in a bundle made it&#10;an easy impulse buy. I think I really like what it&amp;rsquo;s doing, but at this point I&#10;think my Marvel fatigue has morphed into an active dislike of these characters,&#10;so I didn&amp;rsquo;t stick with it.&lt;/p&gt;&#10;&lt;h4 id="nier-reincarnation-ios"&gt;NieR Reincarnation (iOS)&lt;/h4&gt;&#10;&lt;p&gt;I was determined to play through this game before it shut down in April of this&#10;year. This didn&amp;rsquo;t happen - I only played for a couple of hours, and didn&amp;rsquo;t&#10;particularly enjoy my time with it. Now the game&amp;rsquo;s gone forever. What are we&#10;doing here lads?&lt;/p&gt;&#10;&lt;h4 id="hi-fi-rush-xboxpc"&gt;Hi-Fi RUSH (Xbox/PC)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;ll not go over the deeply frustrating story of what has happened to this&#10;creative team this year because other people have written far more eloquently&#10;about it. I&amp;rsquo;ll just say that the game is wonderful and definitely worth a go,&#10;especially if you use Game Pass. The level of care that&amp;rsquo;s gone into the art&#10;in particular is just lovely, and the writing of the characters manages to make&#10;them fun to be around without falling off the edge into &amp;ldquo;deeply irritating&amp;rdquo;.&lt;/p&gt;&#10;&lt;h4 id="celeste-64-pc"&gt;Celeste 64 (PC)&lt;/h4&gt;&#10;&lt;p&gt;This was a surprise treat in the grey cold months of the new year and it&#10;manages to add that third dimension into Celeste in a way that&amp;rsquo;s very consistent&#10;with how the original plays. The music is lovely, too.&lt;/p&gt;&#10;&lt;h4 id="persona-3-reload-xbox"&gt;Persona 3 Reload (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;I was excited for P3R as I&amp;rsquo;d never played that entry in the series and, after&#10;wrapping Persona 5, was quite hungry for Another One Of Those. P3 is certainly&#10;interesting, and you can see where it&amp;rsquo;s backported what worked in P5 back to the&#10;earlier entry, but I found exploring the dungeon to be even more of a slog than&#10;it was in P4G so I sort of lost interest.&lt;/p&gt;&#10;&lt;p&gt;Thankfully, as we&amp;rsquo;ll see later this year, Persona players have eaten extremely&#10;well.&lt;/p&gt;&#10;&lt;h4 id="helldivers-2-pc"&gt;Helldivers 2 (PC)&lt;/h4&gt;&#10;&lt;p&gt;I got into this at launch and played it a little bit, but haven&amp;rsquo;t really gone&#10;back yet - I mostly play games in the morning before work which doesn&amp;rsquo;t sync up&#10;very well for multiplayer titles. I really enjoyed what I did play of it; my&#10;favourite thing is the diving mechanic which is essentially just an instant&#10;punchline button when you haven&amp;rsquo;t the spatial awareness to avoid, say, diving&#10;straight off a cliff.&lt;/p&gt;&#10;&lt;h4 id="witcher-3-blood-and-wine-pc"&gt;Witcher 3: Blood and Wine (PC)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;ve not got terribly far into it but so far Geralt&amp;rsquo;s final(?) adventure is&#10;a real palate cleanse after the grim dark fantasy of the base game. Toussaint&#10;is lively and colourful and sunny and&amp;hellip;what? Oh. Gross. OK, it&amp;rsquo;s still The&#10;Witcher.&lt;/p&gt;&#10;&lt;h4 id="nightingale-pc"&gt;Nightingale (PC)&lt;/h4&gt;&#10;&lt;p&gt;I did a little bit of work on this game back in the day and I&amp;rsquo;m so happy to see&#10;it finally coming out. Survival games aren&amp;rsquo;t really my thing so I didn&amp;rsquo;t play&#10;a huge amount of it, but it&amp;rsquo;s a stunning thing to look at.&lt;/p&gt;&#10;&lt;h4 id="mars-after-midnight-playdate"&gt;Mars After Midnight (Playdate)&lt;/h4&gt;&#10;&lt;p&gt;I confess I don&amp;rsquo;t get a huge amount of use out of the Playdate - I adore the&#10;little thing but for some reason none of the games on it proved to be much more&#10;than a brief distraction. That is until Mars After Midnight, which takes the&#10;ideas Pope had with Papers, Please and twists them into something really fun&#10;and new. An essential purchase, if you have the hardware.&lt;/p&gt;&#10;&lt;h4 id="llamasoft-the-jeff-minter-story-pc"&gt;Llamasoft: The Jeff Minter Story (PC)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;ve long admired Jeff Minter from afar - particularly for his videos of sheep&#10;on his &lt;a href="https://toot.wales/@llamasoft_ox/113741760742481841"&gt;Mastodon&lt;/a&gt; account.&#10;I hadn&amp;rsquo;t really played many of his games, though.&lt;/p&gt;&#10;&lt;p&gt;This collection from Digital Eclipse - part of their Gold Master series which is&#10;just such a wonderful idea - brings together Minter&amp;rsquo;s foundational work in a&#10;package that runs on modern hardware. Far more than that though, it features&#10;interviews, stories, artwork from the archives and more besides. In an age when&#10;game preservation is in real danger it&amp;rsquo;s projects like this that are carrying&#10;the torch for careful and considered archiving. A treat.&lt;/p&gt;&#10;&lt;h4 id="warioware-twisted-gba"&gt;WarioWare: Twisted! (GBA)&lt;/h4&gt;&#10;&lt;p&gt;I bought quite a lot of Game Boy games in Japan - they don&amp;rsquo;t seem to have been&#10;infected with Collector Brainrot quite so much there so most games are a few&#10;quid - but this was the most exciting find. I don&amp;rsquo;t think this game ever came&#10;out in Europe, and the force feedback and tilt sensor in the cartridge means&#10;it&amp;rsquo;s one of those titles that would be very difficult to play in an emulator.&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s WarioWare, so it&amp;rsquo;s fantastic fun. It&amp;rsquo;s in Japanese, too, which is great&#10;practice.&lt;/p&gt;&#10;&lt;h4 id="dragon-age-origins-dlc-pc"&gt;Dragon Age Origins DLC (PC)&lt;/h4&gt;&#10;&lt;p&gt;After wrapping up Origins late last year I decided to dive on into the DLC. Most&#10;of it is pretty underwhelming, but the big expansion, Awakening, might actually&#10;be better than the base game. It introduces you to Anders, who is important in&#10;Dragon Age 2, and Ser Pounce-a-lot, who is important in general.&lt;/p&gt;&#10;&lt;h4 id="dragons-dogma-2-pc"&gt;Dragon’s Dogma 2 (PC)&lt;/h4&gt;&#10;&lt;p&gt;I can&amp;rsquo;t make any comparisons to the first game because I haven&amp;rsquo;t played it, but&#10;I really enjoyed Dragon&amp;rsquo;s Dogma 2. I find its world, despite it feeling somehow&#10;much more dangerous than many fantasy games, to be a very soothing place to&#10;wander around. Fundamentally it&amp;rsquo;s a game about trying to go for a nice walk but&#10;things keep happening. I can empathise.&lt;/p&gt;&#10;&lt;h4 id="botany-manor-pc"&gt;Botany Manor (PC)&lt;/h4&gt;&#10;&lt;p&gt;While the puzzles in Botany Manor don&amp;rsquo;t ever reach a level of complexity to&#10;really set it apart, it&amp;rsquo;s a nicely made thing and doesn&amp;rsquo;t outstay its welcome.&lt;/p&gt;&#10;&lt;h4 id="animal-well-pc"&gt;Animal Well (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2024/05/10-145849-1.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Animal Well is a staggering achievement. It&amp;rsquo;s moody, baffling, whimsical and very&#10;clever. Every bit of it is polished to such a fine degree, from the pixel art to&#10;the tiny little haptic feedback when you jump. It also looks absolutely&#10;&lt;em&gt;stunning&lt;/em&gt; on an OLED display, its beautiful lighting contrasted against the&#10;inky darkness of the well. I haven&amp;rsquo;t finished it yet but I will, I promise.&lt;/p&gt;&#10;&lt;p&gt;Every time I play this game I have a desire to go and do some game programming.&#10;Inspiring stuff.&lt;/p&gt;&#10;&lt;h4 id="destiny-2-the-final-shape-pc"&gt;Destiny 2: The Final Shape (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2024/10/16-220516-1.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;They did it. The mad bastards, they did it, they actually stuck the landing.&lt;/p&gt;&#10;&lt;p&gt;I had such terrifyingly low expectations for the last expansion in Destiny&amp;rsquo;s big&#10;story arc but it exceeded all of them and went straight to the top of the list&#10;for me. I think it&amp;rsquo;s better than Forsaken, even.&lt;/p&gt;&#10;&lt;p&gt;The only downside to the story ending on such a perfect note is that my&#10;motivation to play Destiny sort of vanished; the new Episodes are neat but I&#10;just haven&amp;rsquo;t felt able to prioritise them over the mountain of other games I&#10;wanted to play this year. I think this speaks to the inherent difficulty of&#10;storytelling in a live-service game.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m sure I&amp;rsquo;ll get back into it but after sweeping layoffs I&amp;rsquo;m worried about the&#10;future of Destiny, and of Bungie as a whole. Let&amp;rsquo;s see.&lt;/p&gt;&#10;&lt;h4 id="phoenix-wright-ace-attorney-3ds"&gt;Phoenix Wright: Ace Attorney (3DS)&lt;/h4&gt;&#10;&lt;p&gt;I played this on a friend&amp;rsquo;s DS on a school trip nearly twenty years ago, and&#10;while I barely remembered anything from it I did remember it was something&#10;special. The trilogy release on 3DS means I can revisit it and I&amp;rsquo;m having a&#10;wonderful time with it. The script is wonderfully silly but what I think&#10;somehow elevates it is the localisation, where they&amp;rsquo;ve taken a game &lt;em&gt;clearly&lt;/em&gt;&#10;set in Japan and tried to make it understandable to an American audience,&#10;rendering it slightly absurd in a way that I find delightful.&lt;/p&gt;&#10;&lt;h4 id="dungeons-of-hinterland-pc"&gt;Dungeons of Hinterland (PC)&lt;/h4&gt;&#10;&lt;p&gt;I was drawn in by the art, first and foremost, after seeing it in a preview in&#10;EDGE what must now be years ago. I had a good time with this one, I like what&#10;it&amp;rsquo;s doing with the setting and the dungeon bits feel unique, but some aspects&#10;like the relationships with characters in the town feel a little underbaked.&#10;It&amp;rsquo;s a nice chillout game so I may well go back to it.&lt;/p&gt;&#10;&lt;h4 id="balatro-pc-ios"&gt;Balatro (PC, iOS)&lt;/h4&gt;&#10;&lt;p&gt;Look, you know all about this one. It&amp;rsquo;s very good.&lt;/p&gt;&#10;&lt;h4 id="gears-of-war-2-xbox"&gt;Gears of War 2 (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;The most important thing about Marcus Fenix is that even though he&amp;rsquo;s a massive&#10;ham dude who chops insects up with a chainsaw, he&amp;rsquo;s also very polite and always&#10;says &amp;ldquo;thank you&amp;rdquo; to Anya.&lt;/p&gt;&#10;&lt;h4 id="flock-pc"&gt;Flock (PC)&lt;/h4&gt;&#10;&lt;p&gt;Games about flying are really hard to make, I reckon. You either give the player&#10;a huge amount of control, which makes the game harder to play, or you simplify&#10;it, at risk of getting to the point where it&amp;rsquo;s not flight at all.&lt;/p&gt;&#10;&lt;p&gt;Flock does the latter, and it works perfectly. Height is not something you have&#10;a lot of control over, but the feeling of flying in this game is just so joyful,&#10;bird-thing singing, creatures streaming out behind you, that you don&amp;rsquo;t mind. If&#10;I&amp;rsquo;m stressed out or having a bad day I just pop Flock on and zoom around a&#10;little.&lt;/p&gt;&#10;&lt;p&gt;There&amp;rsquo;s a whole creature collecting game here too, and it&amp;rsquo;s really neat, but&#10;even if you choose not to engage with it it&amp;rsquo;s a lovely time.&lt;/p&gt;&#10;&lt;h4 id="1000xresist-pc"&gt;1000xRESIST (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2024/12/games-played-2024/1000xresist.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I don&amp;rsquo;t really know what I can say about 1000xRESIST that hasn&amp;rsquo;t been said by&#10;much better writers than me. This is a game about authority, diaspora, identity,&#10;the COVID-19 pandemic, democracy, religion, parenthood and about sixty other&#10;things and it manages to fit all this within a sub-10-hour runtime in a way that&#10;feels perfectly coherent. It is a damn near flawless video game and you owe it&#10;to yourself to play it.&lt;/p&gt;&#10;&lt;h4 id="thank-goodness-youre-here-pc"&gt;Thank Goodness You&amp;rsquo;re Here! (PC)&lt;/h4&gt;&#10;&lt;p&gt;I guffawed out loud at this several times. I also think the criticisms of some&#10;of its humour being quite cruel are valid. It&amp;rsquo;s not one for everyone but if it&amp;rsquo;s&#10;for you it&amp;rsquo;s really really for you.&lt;/p&gt;&#10;&lt;h4 id="loddlenaut-pc"&gt;Loddlenaut (PC)&lt;/h4&gt;&#10;&lt;p&gt;It&amp;rsquo;s a cute little game about cleaning up an ocean. It runs really nicely on the&#10;Steam Deck too, so it became a good bedtime game for a while.&lt;/p&gt;&#10;&lt;h4 id="dragon-age-2-pc"&gt;Dragon Age 2 (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2024/12/04-merrill-1.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I never played DA2 back in the day - I was put off it by the reviews at the&#10;time, if I remember rightly - and in hindsight that was the wrong decision. I&#10;&lt;em&gt;really&lt;/em&gt; liked Dragon Age 2. I can honestly say I preferred its streamlined,&#10;snappier combat to that of Origins, and what the story perhaps lacks in scope&#10;prepared to its predecessor it more than makes up for in sheer focus.&lt;/p&gt;&#10;&lt;p&gt;Yes, there is some repetitive level design, but the fact this game was made in&#10;basically a year is staggering. It&amp;rsquo;s such a noticeable technical leap above&#10;Origins.&lt;/p&gt;&#10;&lt;p&gt;I played the DLC too - Mark of the Assassin was a bit of a disappointment but I&#10;enjoyed Legacy with its clear lead into Inquisition.&lt;/p&gt;&#10;&lt;h4 id="tactical-breach-wizards-pc"&gt;Tactical Breach Wizards (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2024/12/games-played-2024/tbw.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Tom Francis is your favourite game designer&amp;rsquo;s favourite game designer. I loved&#10;Heat Signature so I knew Tactical Breach Wizards would be mechanically extremely&#10;good, but what surprised me was how wonderfully realised the story and&#10;characters are, and how deeply funny it is.&lt;/p&gt;&#10;&lt;p&gt;I often find myself getting stuck in these sorts of games - tactics might be my&#10;favourite genre but I&amp;rsquo;m not actually &lt;em&gt;good&lt;/em&gt; at them. TBW&amp;rsquo;s main missions are&#10;a pretty gentle challenge, but the added objectives (e.g. &amp;ldquo;defenestrate three&#10;enemies&amp;rdquo;) means I find myself running the levels again and again trying to&#10;perfect them, something I don&amp;rsquo;t normally do. It&amp;rsquo;s a rare game that turns me&#10;into a completionist, and TBW manages that with ease.&lt;/p&gt;&#10;&lt;p&gt;There&amp;rsquo;s been a recent patch with updated controller support so I need to go back&#10;and see how it plays on the Steam Deck.&lt;/p&gt;&#10;&lt;h4 id="astro-bot-ps5"&gt;Astro Bot (PS5)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2024/12/games-played-2024/astro.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I don&amp;rsquo;t really have a big history with Sony or PlayStation - I had a PSP but my&#10;first &amp;ldquo;proper&amp;rdquo; PlayStation was the PS4 - so the &amp;ldquo;nostalgia bait&amp;rdquo; in this game&#10;didn&amp;rsquo;t really do much for me. It&amp;rsquo;s to its enormous credit, then, that this&#10;doesn&amp;rsquo;t really matter. Astro Bot is a wonderful 3D platformer. It&amp;rsquo;s joyous.&#10;While Astro&amp;rsquo;s moveset doesn&amp;rsquo;t have the sheer expressivity of a 3D Mario, the&#10;level design is top-tier. I ended up 100%-ing it - another rare game to make me&#10;want to do this. It&amp;rsquo;s also the first game I can remember that Lucy has actually&#10;asked me to play because she enjoys watching it, though she&amp;rsquo;s not played it&#10;herself (yet).&lt;/p&gt;&#10;&lt;p&gt;The base levels are pretty gentle, but some of the bonus levels can be&#10;unforgiving, and the final one of these is tough as nails. My pal Matt has been&#10;absolutely stomping the speedrun leaderboards, too. Something for everyone here.&lt;/p&gt;&#10;&lt;h4 id="nine-sols-pc"&gt;Nine Sols (PC)&lt;/h4&gt;&#10;&lt;p&gt;I think Nine Sols may be one of the best games on this list. Unfortunately it&amp;rsquo;s&#10;also definitely the hardest. I went into it thinking it was another&#10;Souls-inspired Metroidvania, and it &lt;em&gt;kind of&lt;/em&gt; is, but for the most part the&#10;combat is inspired by a different From game: Sekiro, which I love but cannot&#10;get past the second or third boss. I fear this game will have the same fate for&#10;me, but if you&amp;rsquo;re into this sort of thing don&amp;rsquo;t miss it.&lt;/p&gt;&#10;&lt;h4 id="isles-of-sea-and-sky-pc"&gt;Isles of Sea and Sky (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2024/12/games-played-2024/isles.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m not very good at puzzle games, and I am particularly not good at&#10;Sokoban-style games, which require an ability to visualise future board states&#10;in a way that I struggle with (I am also bad at chess). It&amp;rsquo;s to Isles&amp;rsquo; credit,&#10;then, that I&amp;rsquo;ve got a decent amount of the way through it and I&amp;rsquo;m having a great&#10;time. There are some truly devilish puzzles in here for you sickos out there.&lt;/p&gt;&#10;&lt;h4 id="the-legend-of-zelda-echoes-of-wisdom-switch"&gt;The Legend of Zelda: Echoes of Wisdom (Switch)&lt;/h4&gt;&#10;&lt;p&gt;The mechanical twist is very clever, though I found myself coming up with much&#10;less creative solutions than I wanted to simply because &amp;ldquo;build a bridge of beds&amp;rdquo;&#10;is so remarkably effective in most cases. One to revisit once there&amp;rsquo;s hardware&#10;that can run it properly, I think.&lt;/p&gt;&#10;&lt;h4 id="cyberpunk-2077-pc"&gt;Cyberpunk 2077 (PC)&lt;/h4&gt;&#10;&lt;p&gt;I remember a time when we used to refer to everything in games as &amp;ldquo;immersive&amp;rdquo;,&#10;to the point where the word sort of lost all meaning. I finally got around to&#10;Cyberpunk this year and, while I&amp;rsquo;ve only played a few hours, it is really&#10;bloody immersive, isn&amp;rsquo;t it? I&amp;rsquo;m having a great time.&lt;/p&gt;&#10;&lt;h4 id="metaphor-refantazio-pc"&gt;Metaphor: ReFantazio (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2024/12/games-played-2024/metaphor.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Metaphor scratches that itch I&amp;rsquo;ve had since wrapping Persona 5, and it does so&#10;with such confidence and verve it&amp;rsquo;s hard not to be impressed, even if I&amp;rsquo;m so far&#10;less invested in the characters than I was in P5. It takes a lot of the base&#10;loops and patterns of Persona and then sands off the rough edges. Dungeon crawls&#10;are a treat thanks to the real-time melee attacks, and the calendar seems to be&#10;just that bit more generous than Persona, allowing room to actually do&#10;everything you want to, as long as you manage your time right. I&amp;rsquo;ll report back&#10;in, ooh, 80 hours.&lt;/p&gt;&#10;&lt;h4 id="pokemon-tcgp-ios"&gt;Pokemon TCGP (iOS)&lt;/h4&gt;&#10;&lt;p&gt;Oh I do enjoy opening the packs, I do. I got the shiny Mewtwo and it was lovely.&#10;It&amp;rsquo;s a pure, manipulative dopamine hit, though I will say I&amp;rsquo;ve not put any real&#10;money into it nor has the game really pushed me to do so.&lt;/p&gt;&#10;&lt;p&gt;I tried playing the actual card game and it&amp;rsquo;s fine. It translates - and&#10;flattens - the Pokemon mechanics in a way that makes sense. I just don&amp;rsquo;t find it&#10;particularly interesting strategically; I&amp;rsquo;m sure it is for stronger players&#10;but when I&amp;rsquo;m just playing against the AI it seems to come down to just&#10;chipping away at HP. Which I guess is a bit like the games, sometimes.&lt;/p&gt;&#10;&lt;h4 id="alan-wake-2-dlc-ps5"&gt;Alan Wake 2 DLC (PS5)&lt;/h4&gt;&#10;&lt;p&gt;I played The Lake House first, and it was neat. It was a little overreliant on&#10;the frustrating combat controls - much like the base game - but I really&#10;enjoyed the characters and the links back to Control.&lt;/p&gt;&#10;&lt;p&gt;The other DLC, Night Springs, absolutely whips. It&amp;rsquo;s so clear the developers&#10;were flexing here and also just having a lot of fun. I did too.&lt;/p&gt;&#10;&lt;h4 id="indiana-jones-xbox"&gt;Indiana Jones (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;I really did think this was going to be an Uncharted but it is, thankfully, much&#10;closer to MachineGames&amp;rsquo; take on Wolfenstein. I don&amp;rsquo;t even care about Indiana&#10;Jones much at all but I&amp;rsquo;ve really fallen for this one so far. MachineGames truly&#10;make the best &amp;ldquo;whack a fascist over the head with a thing&amp;rdquo; games on the planet,&#10;and in the current climate that&amp;rsquo;s a salve indeed.&lt;/p&gt;&#10;&lt;h4 id="arctic-eggs-pc"&gt;Arctic Eggs (PC)&lt;/h4&gt;&#10;&lt;p&gt;This is lovely and short with some delightfully absurd dialog and worldbuilding.&#10;Arctic Eggs utterly refuses to explain itself and I love that.&lt;/p&gt;&#10;&lt;h4 id="arco-pc"&gt;Arco (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2024/12/games-played-2024/arco.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Arco took me by surprise right at the end of the year. I&amp;rsquo;d heard it was good but&#10;was unprepared for &lt;em&gt;how&lt;/em&gt; good - when the plane touched town in Belfast I&#10;resented that I&amp;rsquo;d have to put the game away.&lt;/p&gt;&#10;&lt;p&gt;Pixel art can feel a little tired of late, but when it&amp;rsquo;s good, it&amp;rsquo;s &lt;em&gt;really&lt;/em&gt;&#10;good. Arco is a beautiful game, held up by a robust and surprising tactics&#10;system. It&amp;rsquo;s a little bit Into The Breach and a big bit its own thing. Don&amp;rsquo;t&#10;miss this one, I think it&amp;rsquo;s flown under a lot of radars.&lt;/p&gt;&#10;&lt;h4 id="mouthwashing-pc"&gt;Mouthwashing (PC)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;d been meaning to get to Mouthwashing all year and finally managed it in the&#10;Christmas break. It&amp;rsquo;s a tight little psychological horror game that doesn&amp;rsquo;t take&#10;itself overly seriously. I enjoyed it, but I think I went in expecting something&#10;working in a similar space to Signalis, and it isn&amp;rsquo;t that.&lt;/p&gt;&#10;&lt;h2 id="games-i-finished-in-2024"&gt;Games I finished in 2024&lt;/h2&gt;&#10;&lt;p&gt;Here are a few games I started before 2024 - in some cases a &lt;em&gt;long&lt;/em&gt; time&#10;before - but I have Thoughts and this is my website and you can&amp;rsquo;t tell me shit&#10;because I don&amp;rsquo;t have a comments section.&lt;/p&gt;&#10;&lt;h4 id="the-witcher-3-pc"&gt;The Witcher 3 (PC)&lt;/h4&gt;&#10;&lt;p&gt;I don&amp;rsquo;t know what I can say about The Witcher 3 that hasn&amp;rsquo;t been said a million&#10;times. I&amp;rsquo;ve been playing this game off and on since about 2016, and when the&#10;final credits rolled I genuinely felt bereft. An all-timer.&lt;/p&gt;&#10;&lt;h4 id="yakuza-0-pc"&gt;Yakuza 0 (PC)&lt;/h4&gt;&#10;&lt;p&gt;Similar story here actually - took years to finish this but savoured every bit&#10;of it. Certainly the high point of the series from what I&amp;rsquo;ve played, at least&#10;mechanically, but I&amp;rsquo;m still excited to play through the rest and see these&#10;characters grow and develop.&lt;/p&gt;&#10;&lt;p&gt;I had the opportunity to visit Kabukicho this year - the area of Tokyo that&#10;Kamurocho is based on - and was utterly delighted at being able&#10;to navigate to the Golden Gai without looking at my phone. A singular place in&#10;the medium.&lt;/p&gt;&#10;&lt;h4 id="god-of-war-ragnarok-ps5"&gt;God of War Ragnarok (PS5)&lt;/h4&gt;&#10;&lt;p&gt;I wrapped this one and actually did pretty much every side-quest, which is rare&#10;for me. I think the main story is perhaps a little over-long, but it ties&#10;everything together in a nice way which, in an age of forever franchises, feels&#10;almost bold.&lt;/p&gt;&#10;&lt;h4 id="hades-pc"&gt;Hades (PC)&lt;/h4&gt;&#10;&lt;p&gt;I think it&amp;rsquo;s pretty widely accepted at this point that Hades&amp;rsquo; story doesn&amp;rsquo;t&#10;really kick off until you escape for the first time, but doing so felt like&#10;such a monumental achievement based on how bad I am at this game that I feel&#10;happy including it in this section. The difficulty seems to ramp way up from&#10;this point on, so this might be my stop, but I enjoyed the ride.&lt;/p&gt;&#10;&lt;h4 id="alan-wake-2-ps5"&gt;Alan Wake 2 (PS5)&lt;/h4&gt;&#10;&lt;p&gt;I didn&amp;rsquo;t really like Alan Wake 2 when I played it last year, which peaked with&#10;the now-famous musical number which I thought was deeply unearned in comparison&#10;to Control&amp;rsquo;s Ashtray Maze. I found the combat really unsatisfying and the whole&#10;thing just felt clunky to control in a way I associate with Rockstar games or&#10;software designed for Windows.&lt;/p&gt;&#10;&lt;p&gt;In the end I really came around on it, mainly due to the story and fantastic&#10;DLC. Like the first game, I will probably struggle to relate to anyone what&#10;actually &lt;em&gt;happened&lt;/em&gt; in Alan Wake 2, but I actually briefly considered a replay&#10;for The Final Draft, which says a lot about how far my feelings on it shifted.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;Did I miss your favourite? Something I need to know about? Feel free to yell&#10;at me on &lt;a href="https://bsky.app/profile/joelotter.com/post/3lem6bswyj22x"&gt;Bluesky&lt;/a&gt;&#10;or &lt;a href="https://otter.garden/@joel/113747715567316188"&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;I hope 2025 is better than this year, for all of us.&lt;/p&gt;</content:encoded></item><item><title>Is Bluesky in the Fediverse?</title><link>https://www.joelotter.com/posts/2024/09/bluesky-fediverse/</link><pubDate>Fri, 13 Sep 2024 23:36:16 +0100</pubDate><guid>https://www.joelotter.com/posts/2024/09/bluesky-fediverse/</guid><description>And does it even really matter?</description><content:encoded>&lt;p&gt;Bluesky is having A Moment right now, and my feed is getting busier with games&#10;industry folks. It&amp;rsquo;s lovely! I&amp;rsquo;m a little sad Mastodon didn&amp;rsquo;t get the same love&#10;but Bluesky is undoubtedly easier to use.&lt;/p&gt;&#10;&lt;p&gt;At the same time, another challenger social media platform is having a different&#10;kind of moment. Cohost is &lt;a href="https://web.archive.org/web/20240913032618/https://cohost.org/staff/post/7611443-cohost-to-shut-down"&gt;being shut down&lt;/a&gt; - the creators are understandably unable to sustain funding for it.&lt;/p&gt;&#10;&lt;p&gt;I didn&amp;rsquo;t use Cohost much but it seemed great. Communities being lost is always&#10;sad. The survivability of online spaces is therefore very much at the front of&#10;my brain this week, and while it&amp;rsquo;s highly inconsiderate to tell those mourning&#10;Cohost that centralisation was their core problem it is, nonetheless, partially&#10;true.&lt;/p&gt;&#10;&lt;p&gt;If we build a new community on Bluesky, how likely is it to die? How could it be&#10;saved? What are its extinction events, and how can they be mitigated?&lt;/p&gt;&#10;&lt;p&gt;To answer these questions we need to look at federation, at the Fediverse, at&#10;how Bluesky&amp;rsquo;s underlying technologies work and how those intersect with the push&#10;and pull of human beings. It&amp;rsquo;s a rabbit hole. Let&amp;rsquo;s dig.&lt;/p&gt;&#10;&lt;h2 id="what-is-the-fediverse"&gt;What is the Fediverse?&lt;/h2&gt;&#10;&lt;p&gt;&amp;ldquo;The Fediverse&amp;rdquo;, or &amp;ldquo;Fedi&amp;rdquo; to its close friends, is a term rather like &amp;ldquo;the web&amp;rdquo;&#10;that is quite hard to define without getting into rules and exceptions.&lt;/p&gt;&#10;&lt;p&gt;The most common definition seems to be &amp;ldquo;a network of instances of software that&#10;talk to one another via the ActivityPub protocol&amp;rdquo;. This is the one you&amp;rsquo;ll see&#10;most often and is the one Fedi people mean when they talk about Fedi. They mean&#10;Mastodon, Pleroma, Pixelfed, Lemmy, and many more.&lt;/p&gt;&#10;&lt;p&gt;However, there are often exceptions made to this rule that make &amp;ldquo;the Fediverse&amp;rdquo;&#10;much less clean-cut. Threads, for example, &lt;em&gt;can&lt;/em&gt; communicate via ActivityPub (in&#10;a publish-only fashion at the time of writing) but is usually considered&#10;separate to, or a threat to, the Fediverse. Diaspora has its own protocol, but&#10;usually &lt;em&gt;is&lt;/em&gt; considered part of the Fediverse, as are other protocols within the&#10;lineage of ActivityPub such as OStatus.&lt;/p&gt;&#10;&lt;div class="pure-u-1 pure-u-lg-1-2"&gt;&#10;&lt;img src="https://upload.wikimedia.org/wikipedia/commons/6/68/A_view_into_the_Fediverse.png"/&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;&lt;em&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; via Wikimedia Commons&lt;/em&gt;&lt;/p&gt;&#10;&lt;p&gt;Complicating this further is the use of bridge software like &lt;a href="https://fed.brid.gy/"&gt;Bridgy Fed&lt;/a&gt;, which can translate between protocols so that users on&#10;Bluesky can talk to those on Mastodon.&lt;/p&gt;&#10;&lt;p&gt;My view is that most definitions of &amp;ldquo;Fediverse&amp;rdquo; are unhelpful. Instead it might&#10;be better to look at it not as a list of technologies but as a measure of how&#10;easily it can be killed.&lt;/p&gt;&#10;&lt;p&gt;The exinction event for Cohost was running out of money. The exitinction event&#10;for Twitter, albeit a slow ongoing one, was a buyout by a fascist billionaire.&lt;/p&gt;&#10;&lt;p&gt;The extinction event for the Fediverse is rather harder to define. An&#10;individual Mastodon instance might go dark or a project lose a maintainer, but&#10;the network itself is remarkably resilient. This is rather like the web! The&#10;Fediverse&amp;rsquo;s predators are still out there, though are rather larger and more&#10;nebulous. Perhaps a coordinated embrace-extend-extinguish attack could decimate&#10;its userbase but even in that case there&amp;rsquo;d inevitably be a few instances hanging&#10;on to The Old Ways.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://friendi.ca/"&gt;Friendica&lt;/a&gt; has been around nearly as long as Twitter. I&#10;don&amp;rsquo;t know &lt;em&gt;anyone&lt;/em&gt; who uses it, but it has a community, and as long as someone&#10;is running an instance somewhere it is basically unkillable. One might as well&#10;ponder how to destroy email, though I gather Google is making a spirited&#10;attempt.&lt;/p&gt;&#10;&lt;p&gt;These extinction events are terrible for the preservation of information and&#10;culture, and, perhaps more importantly, are devastating for the communities that&#10;emerge in online spaces.&lt;/p&gt;&#10;&lt;p&gt;This brings us to Bluesky.&lt;/p&gt;&#10;&lt;h2 id="bluesky-and-federation"&gt;Bluesky and federation&lt;/h2&gt;&#10;&lt;p&gt;Federation on Mastodon can be understood as simply as &amp;ldquo;there are instances and&#10;they talk to one another, or don&amp;rsquo;t&amp;rdquo;. Of course, the definition of what a&#10;Mastodon instance actually &lt;em&gt;is&lt;/em&gt; is surprisingly complex in terms of components,&#10;but viewed at a network level it&amp;rsquo;s actually a fairly good model.&lt;/p&gt;&#10;&lt;p&gt;Bluesky is a bit more complicated. There&amp;rsquo;s a really good &lt;a href="https://bsky.social/about/blog/5-5-2023-federation-architecture"&gt;blog post&lt;/a&gt; showing their proposed federation model, and it includes this diagram:&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://bsky.social/about/images/blogposts/federation-architecture-diagram.png" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;This looks scarier than it is.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;A &lt;strong&gt;PDS&lt;/strong&gt; (Personal Data Server) is &lt;em&gt;somewhat&lt;/em&gt; close to Mastodon&amp;rsquo;s idea of an&#10;instance. It stores data, handles user accounts and login, and generally&#10;coordinates your presence in the network&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;&#10;&lt;li&gt;A &lt;strong&gt;Relay&lt;/strong&gt; is a big chonky service that consumes updates from all the PDSes&#10;it knows about and turns them into a firehose stream.&lt;/li&gt;&#10;&lt;li&gt;An &lt;strong&gt;AppView&lt;/strong&gt; consumes the updates from the Relay and renders them into a&#10;format that a client can reasonably make use of. How it does this is very&#10;dependent on the application; Bluesky is considered to be just one Lexicon in&#10;the network.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Feed generators&lt;/strong&gt; and &lt;strong&gt;Labellers&lt;/strong&gt; are separate services that host custom&#10;feeds and account labels respectively. These are starting to pop up a lot,&#10;they&amp;rsquo;re really neat.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;You can think of a Mastodon instance as a PDS and AppView smooshed together&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;.&#10;The idea is that the AppView and PDSes will have rather different scaling&#10;characteristics; I can attest to this as even running my single-digit-user-count&#10;Mastodon instance costs around $20 a month.&lt;/p&gt;&#10;&lt;p&gt;PDSes are open to federation now; there&amp;rsquo;s an &lt;a href="https://github.com/bluesky-social/pds"&gt;example&lt;/a&gt; on GitHub and I know several people&#10;who&amp;rsquo;ve done it. Working with AT Proto is really nice, as I &lt;a href="https://www.joelotter.com/posts/2023/10/bridgy-bluesky/"&gt;discovered&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://docs.bsky.app/blog/blueskys-moderation-architecture"&gt;Moderation&lt;/a&gt; on&#10;Bluesky is also federated. Labellers plug in to the Bluesky&#10;AppView and are used to label content. The user&amp;rsquo;s client can then set what it&#10;does or doesn&amp;rsquo;t want to see. The idea is you&amp;rsquo;d subscribe to the labellers that&#10;you trust to moderate things appropriately for you. This decoupling of&#10;moderation from data storage means you&amp;rsquo;re less exposed to the &amp;ldquo;forum admin mad&#10;with power&amp;rdquo; problem that Mastodon sometimes gets.&lt;/p&gt;&#10;&lt;p&gt;However, defederation on Mastodon is a very powerful tool. There&amp;rsquo;s a reason&#10;nobody sees Truth Social posts on their feed, despite it being technically part&#10;of the Fediverse.&lt;/p&gt;&#10;&lt;p&gt;Bluesky aims to combat this by having centralised moderation within the Bluesky&#10;AppView. Their labellers are applied to all posts that pass through, it&amp;rsquo;s not&#10;something users can unsubscribe from. This is important, but it&amp;rsquo;s also a point&#10;of vulnerability. Let&amp;rsquo;s talk about that.&lt;/p&gt;&#10;&lt;h2 id="the-fascist-billionaire-problem"&gt;The fascist billionaire problem&lt;/h2&gt;&#10;&lt;p&gt;Bluesky is a private company with investors. Twitter was a private, then public&#10;company, and since the buyout is now private again. Mastodon gGmbH is a&#10;non-profit company. Having a source of funding allows these projects to grow&#10;more quickly, but there&amp;rsquo;s an issue of power, and it&amp;rsquo;s important to distinguish&#10;between power over the software and over the network itself.&lt;/p&gt;&#10;&lt;p&gt;When Elon Musk bought Twitter he gained full control over the product and its&#10;entire network; in a centralised service the two are synonymous. Eugen Rochko&#10;could go full fash tomorrow and while this would be a setback for the Fediverse&#10;it would not really damage the network itself.&#10;Even if development on Mastodon entirely ceased - unlikely given its license,&#10;there will always be forks - some other project like Misskey or Pleroma would&#10;fill the gaps.&lt;/p&gt;&#10;&lt;p&gt;Bluesky, as it is right now, exists in a strange middle ground. If the company&#10;was taken over by a bad actor the network itself could continue to function -&#10;AT Proto is an open protocol - but because the AppView is a point of&#10;centralisation and is not, to my knowledge, open-source, I&amp;rsquo;m not sure how&#10;feasible it would be to have Bluesky without Bluesky Social. The relay isn&amp;rsquo;t&#10;open-source either and I&amp;rsquo;m not aware of any independent ones running; at minimum&#10;it would be very expensive to do so.&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;&lt;strong&gt;Update 2024-09-14&lt;/strong&gt;: Thanks to &lt;a href="https://news.ycombinator.com/item?id=41536630"&gt;this HN comment&lt;/a&gt; for pointing me to an open source&#10;implementation of the relay, as well as a &lt;a href="https://whtwnd.com/bnewbold.net/3kwzl7tye6u2y"&gt;blog post&lt;/a&gt;&#10;about costs.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;The relay problems are probably solvable given time - Bluesky themselves do say&#10;their architecture is not prescriptive - but ultimately that Bluesky AppView&#10;is a point of vulnerability in terms of power over the network as it exists&#10;today. A central point of control is useful in moderation terms - Bluesky will&#10;hopefully have a bare minimum level of decency as a global community - but if&#10;that moderation can be subverted that becomes a problem. Like deciding&#10;&amp;ldquo;cisgender&amp;rdquo; is a &lt;a href="https://www.independent.co.uk/news/world/americas/elon-musk-twitter-cisgender-slur-b2438972.html"&gt;slur&lt;/a&gt;.&lt;/p&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;By Imke Senst, Mike Kuketz -&#10;&lt;a href="https://social.tchncs.de/@kuketzblog/110428640620752872"&gt;https://social.tchncs.de/@kuketzblog/110428640620752872&lt;/a&gt;, CC BY-SA 4.0,&#10;&lt;a href="https://commons.wikimedia.org/w/index.php?curid=132360500"&gt;https://commons.wikimedia.org/w/index.php?curid=132360500&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:2"&gt;&#10;&lt;p&gt;See the initial reaction to Bridgy Fed for an example of how fiercely some&#10;will fight for what they see as The Real Fediverse.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:3"&gt;&#10;&lt;p&gt;Bluesky runs on AT Proto rather than ActivityPub. I&amp;rsquo;ve seen some folks&#10;refer to the (largely theoretical so far) ecosystem of AT Proto applications as&#10;&amp;ldquo;the Atmosphere&amp;rdquo;, which is cute.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:4"&gt;&#10;&lt;p&gt;Relays do also exist in the Fediverse, as a way to get smaller instances&#10;to see more of the network, but they&amp;rsquo;re strictly optional and my instance&#10;doesn&amp;rsquo;t use any.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</content:encoded></item><item><title>On not taking it personally</title><link>https://www.joelotter.com/posts/2024/09/not-taking-it-personally/</link><pubDate>Tue, 03 Sep 2024 18:40:44 +0100</pubDate><guid>https://www.joelotter.com/posts/2024/09/not-taking-it-personally/</guid><description>How do we navigate caring about our work when that work is, by nature,&#10;disposable?</description><content:encoded>&lt;p&gt;I once had an engineer, just a few years my junior, come to me quite annoyed.&#10;They had several months previously invested quite a bit of time into planning&#10;and building a feature, and had delivered it on-time and to-spec. That feature&#10;was being removed in order to reduce our maintenance burden; it hadn&amp;rsquo;t been a&#10;hit with customers.&lt;/p&gt;&#10;&lt;p&gt;Software is a strange field. It&amp;rsquo;s a creative discipline&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; where the works we&#10;create are ephemeral, often disposable. I think this is even sadly true in&#10;games, but that&amp;rsquo;s a discussion for another time. I&amp;rsquo;m not sure it&amp;rsquo;s possible to&#10;do good work in &lt;em&gt;any&lt;/em&gt; creative field, no matter its target audience or purpose,&#10;without caring about it at least a little bit. How do you reconcile that with&#10;knowing that any code you write will, some day, no matter how good&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;, end up&#10;in someone&amp;rsquo;s red PR?&lt;/p&gt;&#10;&lt;p&gt;The advice I gave at the time, which I do still stand by, was &lt;strong&gt;&amp;ldquo;we aren&amp;rsquo;t paid&#10;to write code; we&amp;rsquo;re paid to solve problems&amp;rdquo;&lt;/strong&gt;. The feature was being removed,&#10;yes, but in building and shipping that feature we were able to validate (or in&#10;this case &lt;em&gt;invalidate&lt;/em&gt;) some assumptions about what the customers wanted. The&#10;work had done exactly what it was supposed to - get us some new information&#10;about customer behaviours and needs. The fact that it was now being&#10;decommissioned was almost irrelevant - an experiment where the result was a&#10;failure is still a valid, and valuable, exercise.&lt;/p&gt;&#10;&lt;p&gt;I find a lot of those &amp;ldquo;junior vs. senior engineer&amp;rdquo; memes to be a bit cringe and&#10;patronising, but I think there &lt;em&gt;is&lt;/em&gt; a trend that the more experience you accrue&#10;the happier you are to delete large swathes of code. I love it. A red PR makes&#10;my day.&lt;/p&gt;&#10;&lt;p&gt;I think my advice was good, but I&amp;rsquo;m also aware that if I&amp;rsquo;d given it to myself&#10;a few years prior I wouldn&amp;rsquo;t have been able to separate the work from my&#10;emotional attachment to it.&lt;/p&gt;&#10;&lt;h3 id="that-time-i-misunderstood-defensive-programming"&gt;That time I misunderstood &amp;ldquo;defensive programming&amp;rdquo;&lt;/h3&gt;&#10;&lt;p&gt;There was a period in my career, around half a decade ago and lasting around&#10;half a year, of which my overwhelming memory is one of despair. I wasn&amp;rsquo;t in a&#10;great place mentally, and was determined to prove myself with the work I was&#10;doing, at a time when the company was going through a lot of rapid changes.&lt;/p&gt;&#10;&lt;p&gt;This resulted in projects I had a huge emotional investment in being cancelled&#10;or given to other people, making me an anxious, grouchy, pain in the ass to work&#10;with. Challenges to my vision or criticism during the design process were met&#10;with disdain or outright hostility. I dreaded going into the office. The feeling&#10;from my colleagues was probably mutual.&lt;/p&gt;&#10;&lt;p&gt;I somehow maintained the good sense to seek mentorship from my first manager,&#10;still one of the best I&amp;rsquo;ve ever worked with. We covered a lot of good ground,&#10;ranging from professionalism to emotional regulation, but there were a couple of&#10;things in particular that stuck with me.&lt;/p&gt;&#10;&lt;p&gt;The first was a simple way to avoid the defensive, emotional first response to&#10;negative things&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;. It was the understanding that your first response to&#10;something is always going to be emotional, and it&amp;rsquo;ll take a beat for your&#10;rational brain to catch up. Waiting for that rational response before speaking&#10;takes a bit of practice to someone like me who runs a little hot, but I found it&#10;amazing how effective it was.&lt;/p&gt;&#10;&lt;p&gt;A useful phrase I learned when responding to negative feedback, especially while&#10;waiting for the inner lizard to chill, was to say &amp;ldquo;thank you for telling me&#10;this&amp;rdquo;. You don&amp;rsquo;t have to agree or disagree - probably &lt;em&gt;shouldn&amp;rsquo;t&lt;/em&gt;, not right&#10;away - but it gives you the time to absorb the blow and can make the experience&#10;of the person giving the feedback more comfortable. It&amp;rsquo;s also polite&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;, which&#10;is nice.&lt;/p&gt;&#10;&lt;p&gt;The other thing that stuck with me came from a book.&lt;/p&gt;&#10;&lt;h3 id="the-four-agreements"&gt;The Four Agreements&lt;/h3&gt;&#10;&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/The_Four_Agreements"&gt;The Four Agreements&lt;/a&gt; is a&#10;self-help book by Don Miguel Ruiz, and at the time it was recommended to me it&#10;was the first such book I&amp;rsquo;d ever read.&lt;/p&gt;&#10;&lt;p&gt;The author suggests four agreements, to be made with oneself, which are as&#10;follows:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Be impeccable with your word.&lt;/li&gt;&#10;&lt;li&gt;Don&amp;rsquo;t take anything personally.&lt;/li&gt;&#10;&lt;li&gt;Don&amp;rsquo;t make assumptions.&lt;/li&gt;&#10;&lt;li&gt;Always do your best.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;The back half of the book goes into some spirituality stuff that may not land&#10;for everyone, but I found these four rules to be really valuable to keep in my&#10;head. I&amp;rsquo;d remind myself of them before heading out to work, and worked them into&#10;the meditation practice I had at the time&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&#10;&lt;p&gt;The rules are self-balancing. The second one helps you not take negative&#10;feedback so harshly, while the first and fourth prevent &lt;em&gt;that&lt;/em&gt; from making you&#10;an unfeeling monster. The third helps prevent the sort of spiralling the&#10;anxious mind can conjure, and combined with the first results in much healthier&#10;communication.&lt;/p&gt;&#10;&lt;p&gt;This was what got me closer to that impossible balance - caring about the work,&#10;but caring about where the work gets us more, and caring about people most of&#10;all. At that point in time I was in danger of losing my job. This helped me turn&#10;that around. The next year, I ended up getting promoted&lt;sup id="fnref:6"&gt;&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref"&gt;6&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m not trying to sell you on this book, dear reader, but I &lt;em&gt;am&lt;/em&gt; trying to sell&#10;you on deleting code. Go Marie Kondo on your codebase. Thank you, microservice.&#10;Goodbye.&lt;/p&gt;&#10;&lt;div class="footnotes" role="doc-endnotes"&gt;&#10;&lt;hr&gt;&#10;&lt;ol&gt;&#10;&lt;li id="fn:1"&gt;&#10;&lt;p&gt;It is, fight me.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:2"&gt;&#10;&lt;p&gt;We used to have a saying which was &amp;ldquo;you either die in beta or live long&#10;enough to see yourself put &lt;code&gt;/v2&lt;/code&gt; on your APIs&amp;rdquo;.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:3"&gt;&#10;&lt;p&gt;Or &amp;ldquo;snapping at people&amp;rdquo;, as we call it in the business.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:4"&gt;&#10;&lt;p&gt;I reread Paul Ford&amp;rsquo;s essay, &lt;a href="https://ftrain.medium.com/how-to-be-polite-9bf1e69e888c"&gt;How to Be Polite&lt;/a&gt;,&#10;at least once a year.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:5"&gt;&#10;&lt;p&gt;Meditation is wonderful and I am very, very bad at it.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li id="fn:6"&gt;&#10;&lt;p&gt;I quit very shortly afterwards but I promise it was unrelated.&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/div&gt;&#10;</content:encoded></item><item><title>IndieWeb, 2024 edition</title><link>https://www.joelotter.com/posts/2024/08/indieweb/</link><pubDate>Sat, 31 Aug 2024 12:25:29 +0100</pubDate><guid>https://www.joelotter.com/posts/2024/08/indieweb/</guid><description>&lt;p&gt;I&amp;rsquo;ve been running this website on IndieWeb principles and tech for over a year&#10;now, so I thought it was time for an updated edition.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I&amp;rsquo;ve been running this website on IndieWeb principles and tech for over a year&#10;now, so I thought it was time for an updated edition.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ll not rehash everything in &lt;a href="https://www.joelotter.com/posts/2023/03/indieweb/"&gt;the original post&lt;/a&gt;&#10;because it&amp;rsquo;s all still basically relevent (apart from the bit about syndicating&#10;to Twitter, for obvious reasons), but I&amp;rsquo;ll cover some of the new stuff.&lt;/p&gt;&#10;&lt;h2 id="twitter"&gt;Twitter&lt;/h2&gt;&#10;&lt;p&gt;At this point I&amp;rsquo;ve stopped using Twitter entirely, which is probably for the&#10;best. As I noticed last time I took a social media break, Twitter is simply not&#10;the real world, and while a lot of online culture has historically existed&#10;downstream from it, you don&amp;rsquo;t &lt;em&gt;actually&lt;/em&gt; miss anything by not looking at it.&lt;/p&gt;&#10;&lt;p&gt;(I think even the point about internet culture being downstream from Twitter is&#10;increasingly less true in a world dominated by TikTok, a platform I find to be&#10;the digital representation of a headache, but I am now in my thirties and&#10;officially Old so don&amp;rsquo;t listen to me.)&lt;/p&gt;&#10;&lt;p&gt;All of this is to say: this site doesn&amp;rsquo;t syndicate to Twitter any more. I&amp;rsquo;d love&#10;to say this was a moral decision but in reality it was made for me when they&#10;killed their API.&lt;/p&gt;&#10;&lt;h2 id="threads"&gt;Threads&lt;/h2&gt;&#10;&lt;p&gt;I&amp;rsquo;m less fearful of Threads than many Fediverse-aligned folks, and I haven&amp;rsquo;t&#10;made any effort to block it from my Mastodon instance, even following a few&#10;people (mostly tech journalists). The few times I&amp;rsquo;ve looked at Threads it seems&#10;to be a mix of tepid normie takes, weird American liberal politics and truly&#10;bizarre pro-Israel propaganda. I&amp;rsquo;m good, thanks. I am not on Threads save to&#10;park my username for when they let Threads folks follow Fedi users.&lt;/p&gt;&#10;&lt;h2 id="bluesky"&gt;Bluesky&lt;/h2&gt;&#10;&lt;p&gt;It seems wild to think that when I wrote the previous post I didn&amp;rsquo;t have a&#10;Bluesky account but here we are, a year and change later, and it&amp;rsquo;s fully&#10;integrated into my publishing flow as well as maybe actually my favourite&#10;platform.&lt;/p&gt;&#10;&lt;p&gt;The Bluesky team seem to genuinely give a shit about adding moderation and&#10;anti-harassment tools, and while there have certainly been missteps it does seem&#10;to be moving in the right direction and without a lot of the Dorsey-aligned&#10;free-speech absolutism that Fedi folks seem to think it operates on. Jack Dorsey&#10;has expressed disappointment with the direction Bluesky has taken, which is&#10;honestly a pretty solid endorsement.&lt;/p&gt;&#10;&lt;p&gt;I also think AT Proto, the underlying protocol, has several key advantages over&#10;ActivityPub. It&amp;rsquo;s just a shame they aren&amp;rsquo;t compatible, though Ryan has been&#10;doing some &lt;a href="https://snarfed.org/2024-06-24_53250"&gt;excellent work&lt;/a&gt; in bringing&#10;them closer together.&lt;/p&gt;&#10;&lt;p&gt;I &lt;a href="https://www.joelotter.com/posts/2023/10/bridgy-bluesky/"&gt;helped build&lt;/a&gt; the Bluesky integration for&#10;Bridgy, which is now almost full-featured. All my notes go to Mastodon and&#10;Bridgy, and replies and reactions are backfed from both.&lt;/p&gt;&#10;&lt;h2 id="micropub"&gt;Micropub&lt;/h2&gt;&#10;&lt;p&gt;This is the biggest bit of work I&amp;rsquo;ve done to my own setup since I wrote the&#10;previous post. At the time, I wrote:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;The only thing I’d really like to add is some kind of Micropub-compatible&#10;setup where I could post notes as easily as I do through a Mastodon app,&#10;rather than needing to use Git on my computer.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;I managed to implement this using the &lt;a href="https://indieweb.org/Micropub"&gt;Micropub&lt;/a&gt;&#10;spec. I explored a few self-hosted options, but none of them really play super&#10;nice with my static site setup.&lt;/p&gt;&#10;&lt;p&gt;I ended up writing the endpoint myself, in Go, and hosting it on an AWS Lambda,&#10;using an S3 bucket as a temporary uploading point for images using Micropub&amp;rsquo;s&#10;media endpoint. This works quite well, in that it&amp;rsquo;s cheap and integrates nicely&#10;with GitHub, but has a few key pain points:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Working with Lambda kind of sucks. I don&amp;rsquo;t really like serverless tech in&#10;general - I think it ties projects far to closely to specific cloud vendors -&#10;but for a personal project it&amp;rsquo;s &lt;em&gt;fine&lt;/em&gt;. Not good. But fine.&lt;/li&gt;&#10;&lt;li&gt;The publishing flow goes like this:&#10;&lt;ul&gt;&#10;&lt;li&gt;Publish to Micropub endpoint&lt;/li&gt;&#10;&lt;li&gt;Micropub updates Git repository&lt;/li&gt;&#10;&lt;li&gt;GitHub Actions publishes to the web page.&lt;/li&gt;&#10;&lt;li&gt;GHA waits for the page to be publicly visible.&lt;/li&gt;&#10;&lt;li&gt;GHA tells Bridgy to syndicate the post, and updates its POSSE metadata&#10;accordingly.&lt;/li&gt;&#10;&lt;li&gt;I wait for that to get published to the website.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;This is, to my mind, at least a couple more steps than it ought to be, and it&amp;rsquo;s&#10;largely due to my decision to host a static site. It&amp;rsquo;s fine for fire-and-forget,&#10;but then you have to remember to check a few minutes later to see if everything&#10;showed up. I&amp;rsquo;d move back to WordPress, but I really like having a Git repo be my&#10;authoritative data store. I think what I want is essentially a Hugo server. I&#10;don&amp;rsquo;t think that exists.&lt;/p&gt;&#10;&lt;p&gt;For publishing, I use &lt;a href="https://quill.p3k.io"&gt;Quill&lt;/a&gt;, a Micropub webapp by&#10;&lt;a href="https://aaronparecki.com/"&gt;Aaron Parecki&lt;/a&gt;. It&amp;rsquo;s really good - simple enough to&#10;work on poor data connections, and has all the features I need.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve added a couple of things to my Micropub endpoint which are not necessarily&#10;typical and might be considered extensions of the spec, but they&amp;rsquo;re nice for my&#10;use case.&lt;/p&gt;&#10;&lt;p&gt;Firstly, I interpret tags starting with a &lt;code&gt;#&lt;/code&gt; symbol as hashtags, and these get&#10;appended accordingly to the syndicated posts on platforms supporting hashtags.&lt;/p&gt;&#10;&lt;p&gt;Secondly, I interpret the location data in the Micropub request not as a&#10;location that actually gets used, though I do store it, but as a switch to turn&#10;on geotags for photos I upload.&lt;/p&gt;&#10;&lt;p&gt;Photos taken on iPhone contain a lot of EXIF data, and this is neat and I like&#10;it, but I don&amp;rsquo;t want people to be able to find out my precise address from&#10;pictures of my cat. Instead, I strip all the location EXIF data from photos as&#10;part of the site&amp;rsquo;s build process, but include it in the post itself if the&#10;Micropub request includes a location. You can see an example&#10;&lt;a href="https://www.joelotter.com/notes/2024/08/26-castle-ward/"&gt;here&lt;/a&gt; (at the bottom of the post).&lt;/p&gt;&#10;&lt;h2 id="whats-next"&gt;What&amp;rsquo;s next&lt;/h2&gt;&#10;&lt;p&gt;I&amp;rsquo;m pretty happy with the publishing flow at the minute. Something I&amp;rsquo;ve chipped&#10;away at for a while is my own Webmention endpoint, similar to my Micropub&#10;endpoint; Webmention.io is great but doesn&amp;rsquo;t support emojis and I&amp;rsquo;d like to own&#10;that data myself. It&amp;rsquo;s pretty low priority under the ten billion other side&#10;projects I have going, though.&lt;/p&gt;</content:encoded></item><item><title>Deal to Dover</title><link>https://www.joelotter.com/posts/2024/07/deal-to-dover/</link><pubDate>Fri, 19 Jul 2024 09:43:00 +0100</pubDate><guid>https://www.joelotter.com/posts/2024/07/deal-to-dover/</guid><description>&lt;p&gt;I have lived in England for over a decade but have yet to visit&#10;the famous cliffs. It’s Thursday, it’s sunny, I’ve nothing to do until my new&#10;job begins next week, so I get up early and hop on a train to the south coast.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I have lived in England for over a decade but have yet to visit&#10;the famous cliffs. It’s Thursday, it’s sunny, I’ve nothing to do until my new&#10;job begins next week, so I get up early and hop on a train to the south coast.&lt;/p&gt;&#10;&lt;p&gt;It’s been suggested to me to walk from Deal to Dover, rather than the other way&#10;around; this way I’ll get the longest train journey out of the way first, and&#10;will see the scenery become more dramatic as I go.&lt;/p&gt;&#10;&lt;p&gt;I arrive into Deal around half past ten, coat myself in the factor 50 we bought&#10;in a Tokyo konbini, and set off.&lt;/p&gt;&#10;&lt;p&gt;Deal is a fairly typical English coastal town. Quiet, pretty, conservative. It&#10;was important in the age of sail but has become something of a retirement&#10;village since Britain moved on to more lucrative businesses like financial&#10;crimes and privatisation. I didn’t see anyone in their twenties other than a&#10;pair of stern-faced joggers, squinting and pounding against the sun.&lt;/p&gt;&#10;&#10;&lt;img src="https://www.joelotter.com/posts/2024/07/deal-to-dover/1.jpg" alt="A boat on the shoreline. It&amp;#39;s called &amp;#39;Moss Rose&amp;#39;." title="A boat on the shoreline. It&amp;#39;s called &amp;#39;Moss Rose&amp;#39;." /&gt;&#10;&lt;div class="photos-meta"&gt;&#10; &lt;div&gt;&lt;i class="fa fa-camera"&gt;&lt;/i&gt; iPhone 11 Pro back triple camera 6mm f/2&lt;/div&gt;&#10; &lt;div&gt;&lt;i class="fas fa-map-marked-alt"&gt;&lt;/i&gt; &lt;a target="blank" href="https://www.openstreetmap.org/?mlat=51.220794444444444&amp;mlon=1.4043722222222221"&gt;51.22079, 1.40437&lt;/a&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;I follow the coastal path south, steep shingle beach to my left. I pass by a&#10;fairground under construction. An older lady pushes another in a wheelchair,&#10;and they chat about how the helter-skelter was always their favourite. There’s&#10;a lot of past in Deal. The path is lined with memorial benches, enough for the&#10;whole town to take the weight off their feet. I lose count at around a hundred.&#10;One bench is in memory of a teenager, and someone has left a boxed copy of&#10;their favourite game on the bench, which tears something within me.&lt;/p&gt;&#10;&lt;p&gt;I’m reminded of a friend whose daughter thought people were buried under these&#10;benches, which is actually a fair display of deductive reasoning for a&#10;four-year-old. I remember that shortly before her death my grandmother&#10;commissioned a bench in memory of my grandfather. It was intended for the&#10;graveyard, but she kept it in the farmyard because she liked to sit on it. It’s&#10;in the graveyard now and we sit on it when we go to visit her. I hope when I&#10;die I turn into a bench.&lt;/p&gt;&#10;&#10;&lt;img src="https://www.joelotter.com/posts/2024/07/deal-to-dover/2.jpg" alt="A standing stone by the sea." title="A standing stone by the sea." /&gt;&#10;&lt;div class="photos-meta"&gt;&#10; &lt;div&gt;&lt;i class="fa fa-camera"&gt;&lt;/i&gt; iPhone 11 Pro back triple camera 6mm f/2&lt;/div&gt;&#10; &lt;div&gt;&lt;i class="fas fa-map-marked-alt"&gt;&lt;/i&gt; &lt;a target="blank" href="https://www.openstreetmap.org/?mlat=51.19418611111111&amp;mlon=1.403561111111111"&gt;51.19419, 1.40356&lt;/a&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;The scenery is repetitive, but beautiful. The sun is hot and the crickets are&#10;out. Their singing makes me want to go barefoot like I always did as a child,&#10;but this is still England, and dog poo is still a risk, so I don’t. I make good&#10;time on the flat and by the end of the first hour I’ve walked about five&#10;kilometres. I stop for a while at a pub that looks out into sea, the honey&#10;smell of wild parsnip and gorse intoxicating in the heavy air. It’s about a&#10;kilometre after this, round the headland, that I see my first cliff.&lt;/p&gt;&#10;&#10;&lt;img src="https://www.joelotter.com/posts/2024/07/deal-to-dover/3.jpg" alt="The first view of white cliffs poking above the trees." title="The first view of white cliffs poking above the trees." /&gt;&#10;&lt;div class="photos-meta"&gt;&#10; &lt;div&gt;&lt;i class="fa fa-camera"&gt;&lt;/i&gt; iPhone 11 Pro back triple camera 6mm f/2&lt;/div&gt;&#10; &lt;div&gt;&lt;i class="fas fa-map-marked-alt"&gt;&lt;/i&gt; &lt;a target="blank" href="https://www.openstreetmap.org/?mlat=51.18015&amp;mlon=1.4042138888888889"&gt;51.18015, 1.40421&lt;/a&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;I grew up in Northern Ireland, which has something of a monopoly on dramatic&#10;coastlines, so I was prepared to be a little underwhelmed. There is something,&#10;though, about the suddenness with which England suddenly stops and becomes sea,&#10;the starkness of the cliffs against the blue of the sky - I think I understand&#10;a little why the place is considered one end of this island. I climb up a&#10;little, then I’m walking along the tops of the cliffs.&lt;/p&gt;&#10;&lt;p&gt;Onwards. It’s hot. France lurks out at sea somewhere, behind a heat haze, but&#10;grabs my phone with its signal carrier just to prove it’s there. I time-travel&#10;an hour. I wave at a paraglider; they don’t wave back.&lt;/p&gt;&#10;&lt;p&gt;Onwards. I’m into farmland, and definitely into chalk country. I think&#10;immediately of Pratchett’s Wee Free Men, a book whose description of a&#10;connection to the land struck at something so fundamental in me it left me&#10;weeping on the Tube in my early twenties. The bottom of my boots turn white.&lt;/p&gt;&#10;&lt;p&gt;Onwards. Contrails cast shadows in the high noon. I’m playing peekaboo with the&#10;cliffs. I’ll occasionally see their heads poke up to look at me, then duck down&#10;as I get close. I am on the cliffs, but I have not yet really &lt;em&gt;seen&lt;/em&gt; the&#10;cliffs, until I reach St Margaret’s Bay.&lt;/p&gt;&#10;&#10;&lt;img src="https://www.joelotter.com/posts/2024/07/deal-to-dover/4.jpg" alt="The view down into St Margaret&amp;#39;s Bay." title="The view down into St Margaret&amp;#39;s Bay." /&gt;&#10;&lt;div class="photos-meta"&gt;&#10; &lt;div&gt;&lt;i class="fa fa-camera"&gt;&lt;/i&gt; iPhone 11 Pro back triple camera 6mm f/2&lt;/div&gt;&#10; &lt;div&gt;&lt;i class="fas fa-map-marked-alt"&gt;&lt;/i&gt; &lt;a target="blank" href="https://www.openstreetmap.org/?mlat=51.15456666666667&amp;mlon=1.3892583333333333"&gt;51.15457, 1.38926&lt;/a&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;I stop for a while in this stunning place, and regret not bringing my swimming&#10;trunks. I eat too much whitebait at a little pub and buy an ice cream from a&#10;lady at a stall on the beach. We stare at the card machine while it struggles&#10;to get the sky to talk to it. “It has to go to France and back,” the lady says.&#10;“It’ll be fine.”&lt;/p&gt;&#10;&lt;p&gt;I later learn that St Margaret’s Bay is the closest point in Britain to France,&#10;so I feel like I’ve been on holiday.&lt;/p&gt;&#10;&lt;p&gt;Onwards, back on up to the cliffs, through a blob of greenery that still has a&#10;lingering smell of wild garlic despite the season being long past. It’s very&#10;hot now. I’m full of food, and without the cognitive overhead of needing to&#10;navigate - the sea always in sight - my mind bumbles along freely. I pass by&#10;skylark nesting sites, but don’t spot any. The worn paths through the grass,&#10;pure white with chalk in the sun, remind me of the lines on a tennis ball. I&#10;spot the slick hardness of flints embedded in the ground. There’s a lot of past&#10;here too.&lt;/p&gt;&#10;&#10;&lt;img src="https://www.joelotter.com/posts/2024/07/deal-to-dover/5.jpg" alt="The view back along the clifftops; green grass and white chalk." title="The view back along the clifftops; green grass and white chalk." /&gt;&#10;&lt;div class="photos-meta"&gt;&#10; &lt;div&gt;&lt;i class="fa fa-camera"&gt;&lt;/i&gt; iPhone 11 Pro back triple camera 4.25mm f/1.8&lt;/div&gt;&#10; &lt;div&gt;&lt;i class="fas fa-map-marked-alt"&gt;&lt;/i&gt; &lt;a target="blank" href="https://www.openstreetmap.org/?mlat=51.13446111111111&amp;mlon=1.345575"&gt;51.13446, 1.34558&lt;/a&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;Eventually I am within sight of Dover Port. It’s vast, and I can hear it even&#10;from this distance. As I get closer the paths become more busy. I pass by a&#10;probably-literal boatload of German tourists. They look tired and unimpressed.&#10;One older couple stops me along the way, asking “can you see anything from up&#10;there?”. I start to explain that the best views are about an hour back, at the&#10;bay, but they laugh at me, tell me they’ve done their cardio for the day. Fair&#10;enough.&lt;/p&gt;&#10;&#10;&lt;img src="https://www.joelotter.com/posts/2024/07/deal-to-dover/6.jpg" alt="Dover Port with lots of boats and lorries." title="Dover Port with lots of boats and lorries." /&gt;&#10;&lt;div class="photos-meta"&gt;&#10; &lt;div&gt;&lt;i class="fa fa-camera"&gt;&lt;/i&gt; iPhone 11 Pro back triple camera 4.25mm f/1.8&lt;/div&gt;&#10; &lt;div&gt;&lt;i class="fas fa-map-marked-alt"&gt;&lt;/i&gt; &lt;a target="blank" href="https://www.openstreetmap.org/?mlat=51.12665555555556&amp;mlon=1.3323361111111112"&gt;51.12666, 1.33234&lt;/a&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;The descent into Dover takes longer than expected, the port even bigger than I&#10;thought. The guide I found this walk on - the ever-reliable Saturday Walker’s&#10;Club - described it as “cheerless”, which feels unfair, but I will admit the&#10;place does have the air of a town in decline. I don’t know if this can be laid&#10;at the feet of Brexit but it can’t have helped. I don’t see many locals.&lt;/p&gt;&#10;&lt;p&gt;I do find one lovely spot. There’s a street, a little back from the seafront,&#10;where the houses are built up to, and indeed into, the cliffs. Looking up, I&#10;notice a few windows and doors built into them, perhaps fifty metres up. I&#10;don’t know what they’re for, but I find them delightful. Do people live in&#10;there? Is there a whole warren of tunnels and rooms? Most importantly: can I&#10;visit?&lt;/p&gt;&#10;&#10;&lt;img src="https://www.joelotter.com/posts/2024/07/deal-to-dover/7.jpg" alt="A row of colourful terraced houses, with the cliffs behind." title="A row of colourful terraced houses, with the cliffs behind." /&gt;&#10;&lt;div class="photos-meta"&gt;&#10; &lt;div&gt;&lt;i class="fa fa-camera"&gt;&lt;/i&gt; iPhone 11 Pro back triple camera 4.25mm f/1.8&lt;/div&gt;&#10; &lt;div&gt;&lt;i class="fas fa-map-marked-alt"&gt;&lt;/i&gt; &lt;a target="blank" href="https://www.openstreetmap.org/?mlat=51.1265&amp;mlon=1.3261444444444443"&gt;51.12650, 1.32614&lt;/a&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&#10;&#10;&lt;img src="https://www.joelotter.com/posts/2024/07/deal-to-dover/8.jpg" alt="The cliffs, with little doors in them." title="The cliffs, with little doors in them." /&gt;&#10;&lt;div class="photos-meta"&gt;&#10; &lt;div&gt;&lt;i class="fa fa-camera"&gt;&lt;/i&gt; iPhone 11 Pro back triple camera 6mm f/2&lt;/div&gt;&#10; &lt;div&gt;&lt;i class="fas fa-map-marked-alt"&gt;&lt;/i&gt; &lt;a target="blank" href="https://www.openstreetmap.org/?mlat=51.12608055555555&amp;mlon=1.3247888888888888"&gt;51.12608, 1.32479&lt;/a&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;My train back to London is cancelled, and the next one isn’t for an hour, as if&#10;to remind me that I am in fact still in England and not on my holidays. My&#10;phone has switched back to a UK network. My connecting train is cancelled too.&#10;Back to reality, then.&lt;/p&gt;&#10;&lt;div class="strava-embed-placeholder" data-embed-type="activity" data-embed-id="11920631590" data-style="standard"&gt;&lt;/div&gt;&lt;script src="https://strava-embeds.com/embed.js"&gt;&lt;/script&gt;</content:encoded></item><item><title>New job smell</title><link>https://www.joelotter.com/posts/2024/07/new-job-smell/</link><pubDate>Wed, 17 Jul 2024 20:25:35 +0100</pubDate><guid>https://www.joelotter.com/posts/2024/07/new-job-smell/</guid><description>&lt;p&gt;I had my last day at Emitwise yesterday, and now I&amp;rsquo;m in the odd liminal space&#10;that happens before I start my new job next week.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I had my last day at Emitwise yesterday, and now I&amp;rsquo;m in the odd liminal space&#10;that happens before I start my new job next week.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve spent two years now working out of the games industry. I&amp;rsquo;ve really missed&#10;it. There&amp;rsquo;s something about the process of shipping a game - frantic and&#10;frenetic as it is - that you just don&amp;rsquo;t really get in the rest of the technology&#10;industry. My own work on Feud and Growl has been tiding me over but it got to a&#10;point where I felt ready to go back.&lt;/p&gt;&#10;&lt;p&gt;Happily, this coincided with friend of the show Vitor getting in touch to tell&#10;me about &lt;a href="https://companiongroup.io/"&gt;Companion Group&lt;/a&gt;, where he works.&#10;Companion is a small but rapidly growing co-development outfit crewed by some&#10;very talented folks, including a fair few I worked with back at Improbable. My&#10;favourite bit of Improbable was probably building backend services for games&#10;like Scavengers and Nightingale, so it&amp;rsquo;s very exciting to me that I&amp;rsquo;m going to&#10;get to do that kind of work again.&lt;/p&gt;&#10;&lt;p&gt;I start next week. Wish me luck! In the meantime I&amp;rsquo;ll be doing DIY, a process&#10;which apparently never ever ends.&lt;/p&gt;</content:encoded></item><item><title>All my Japan tips</title><link>https://www.joelotter.com/posts/2024/05/japan-tips/</link><pubDate>Mon, 20 May 2024 18:10:28 +0100</pubDate><guid>https://www.joelotter.com/posts/2024/05/japan-tips/</guid><description>I have been to Japan exactly once, so you should definitely listen to me.</description><content:encoded>&lt;p&gt;I got back from Japan about a month ago, and in that time I&amp;rsquo;ve had lots of&#10;people ask me for tips on places to go and things to do. Turns out Japan is very&#10;popular as a tourist destination right now! Who could have imagined the weak Yen&#10;would do this.&lt;/p&gt;&#10;&lt;p&gt;Anyway, I thought I&amp;rsquo;d write a big old list of all my tips and suggestions and&#10;put it on here.&lt;/p&gt;&#10;&lt;div class="toc"&gt;&#10; &lt;nav id="TableOfContents"&gt;&#10; &lt;ul&gt;&#10; &lt;li&gt;&lt;a href="#general-travel-stuff"&gt;General travel stuff&lt;/a&gt;&#10; &lt;ul&gt;&#10; &lt;li&gt;&lt;a href="#transport"&gt;Transport&lt;/a&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;a href="#language"&gt;Language&lt;/a&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;a href="#money"&gt;Money&lt;/a&gt;&lt;/li&gt;&#10; &lt;/ul&gt;&#10; &lt;/li&gt;&#10; &lt;li&gt;&lt;a href="#places"&gt;Places&lt;/a&gt;&#10; &lt;ul&gt;&#10; &lt;li&gt;&lt;a href="#nagoya"&gt;Nagoya&lt;/a&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;a href="#hiroshima-prefecture"&gt;Hiroshima prefecture&lt;/a&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;a href="#osaka"&gt;Osaka&lt;/a&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;a href="#nara"&gt;Nara&lt;/a&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;a href="#kyoto"&gt;Kyoto&lt;/a&gt;&lt;/li&gt;&#10; &lt;li&gt;&lt;a href="#tokyo"&gt;Tokyo&lt;/a&gt;&lt;/li&gt;&#10; &lt;/ul&gt;&#10; &lt;/li&gt;&#10; &lt;/ul&gt;&#10;&lt;/nav&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;h2 id="general-travel-stuff"&gt;General travel stuff&lt;/h2&gt;&#10;&lt;h3 id="transport"&gt;Transport&lt;/h3&gt;&#10;&lt;p&gt;Public transport in Japan is incredible - it&amp;rsquo;s fast, relatively cheap, quiet and&#10;reliable, everything trains in the UK are not.&lt;/p&gt;&#10;&lt;p&gt;You&amp;rsquo;re going to want to make sure you have an IC Card. These are contactless&#10;cards that you top up with cash or a credit card, and they&amp;rsquo;re used for pretty&#10;much all transportation - and you can pay for things in shops and vending&#10;machines with them which is neat. Each city or area has their own - mine is&#10;Manaca because I got it in Nagoya - but they&amp;rsquo;re all compatible with one another.&lt;/p&gt;&#10;&lt;p&gt;If you want to be super prepared and don&amp;rsquo;t mind not having the physical card (I&#10;think they&amp;rsquo;re cute, personally) you can get a Suica or Pasmo card on Apple Pay&#10;before you head out. Have a look in the Apple Wallet app. I&amp;rsquo;m sure there&amp;rsquo;s a&#10;Google equivalent too!&lt;/p&gt;&#10;&lt;p&gt;If you&amp;rsquo;re travelling with luggage, pretty much every station has coin lockers,&#10;though in the bigger cities they&amp;rsquo;re often full - it&amp;rsquo;s worth checking if the&#10;station also has a baggage room, which isn&amp;rsquo;t much more expensive.&lt;/p&gt;&#10;&lt;h4 id="trains"&gt;Trains&lt;/h4&gt;&#10;&lt;p&gt;Trains run regularly and you can just use your IC Card for most of them. The&#10;exception is &amp;ldquo;Limited&amp;rdquo; trains - you might see &amp;ldquo;Limited Express&amp;rdquo; on a sign - in&#10;which case you need to pre-book a seat. I never bothered with these; we never&#10;really had an issue getting seats on trains in Japan.&lt;/p&gt;&#10;&lt;p&gt;There are different speeds, too - Express, Local, Commuter Express and so on.&#10;You&amp;rsquo;ll usually just get the fastest one that stops at the stop you need. I used&#10;Apple Maps a lot for transit in Japan, it&amp;rsquo;s really good for train times and&#10;platforms.&lt;/p&gt;&#10;&lt;p&gt;Do note that on trains in Japan it&amp;rsquo;s generally considered rude to eat, talk&#10;loudly or talk on the phone, which is objectively correct.&lt;/p&gt;&#10;&lt;h4 id="buses"&gt;Buses&lt;/h4&gt;&#10;&lt;p&gt;Buses are a great way to get about and I really enjoyed some of our bus journeys&#10;outside the cities. You get a lot more of the local flavour that way, I think.&lt;/p&gt;&#10;&lt;p&gt;You need to tap on and off buses in Japan, rather than in the UK where it&amp;rsquo;s a&#10;flat fee. You get on at the rear door, tap the IC Card there, then when you&amp;rsquo;re&#10;getting off tap next to the driver. You can pay with cash too if you have exact&#10;change but to be honest I couldn&amp;rsquo;t get my head around the fare system so it&amp;rsquo;s&#10;easier not to.&lt;/p&gt;&#10;&lt;h4 id="shinkansen"&gt;Shinkansen&lt;/h4&gt;&#10;&lt;p&gt;The Shinkansen is the bullet train, and it&amp;rsquo;s amazing. It&amp;rsquo;s super fast and comfy&#10;and fairly affordable for what it is.&lt;/p&gt;&#10;&lt;p&gt;You&amp;rsquo;ll probably want to book Shinkansen tickets separately - it&amp;rsquo;s possible to&#10;hook up your IC Card to a credit card but it&amp;rsquo;s probably more effort than it&amp;rsquo;s&#10;worth. There are different speeds, with Nozomi being the fastest, so make sure&#10;you get the right one. I think the easiest way to buy tickets is at the machines&#10;in stations, as they have an English setting and will walk you through all the&#10;different options and available seats.&lt;/p&gt;&#10;&lt;p&gt;Train tickets and seat reservation tickets will be separate bits of paper, and&#10;you&amp;rsquo;ll need both. If you&amp;rsquo;re travelling with big luggage (big enough to not go in&#10;an overhead rack, basically) then you&amp;rsquo;ll need a special seat. Unfortunately&#10;there are only four of these in each car, so some busy trains might not have any&#10;available.&lt;/p&gt;&#10;&lt;h4 id="taxis"&gt;Taxis&lt;/h4&gt;&#10;&lt;p&gt;We didn&amp;rsquo;t get any taxis in Japan but do be aware they are &lt;em&gt;very&lt;/em&gt; expensive. Plan&#10;your route home so you can avoid needing them!&lt;/p&gt;&#10;&lt;h3 id="language"&gt;Language&lt;/h3&gt;&#10;&lt;p&gt;I&amp;rsquo;ve been learning Japanese for a little while so being in Japan was a wonderful&#10;opportunity to practice. Even if you have only a little bit of Japanese, like&#10;me, folks will really appreciate the effort. If you&amp;rsquo;re used to European&#10;countries where any attempt at the language is met with a dirty look and an&#10;immediate switch to English, don&amp;rsquo;t worry, it&amp;rsquo;s not like that.&lt;/p&gt;&#10;&lt;p&gt;If you want to learn a little Japanese before you go, my strongest&#10;recommendation would actually be to learn Katakana, one of the phonetic scripts&#10;used (mostly) to write loan-words. If you know Katakana you can actually read an&#10;awful lot of the signage around Japan, which is super useful.&lt;/p&gt;&#10;&lt;p&gt;Tofugu have a really great &lt;a href="https://www.tofugu.com/japanese/learn-katakana/"&gt;mnemonics chart&lt;/a&gt; for learning Katakana which I love.&lt;/p&gt;&#10;&lt;p&gt;Otherwise, Google Translate works fairly well, and speaking into a phone to&#10;translate is still, in my opinion, less rude than talking loudly and slowly in&#10;English at people.&lt;/p&gt;&#10;&lt;h3 id="money"&gt;Money&lt;/h3&gt;&#10;&lt;p&gt;Japan is essentially a cash-only country - I knew card and contactless payment&#10;were less popular before I went out but didn&amp;rsquo;t quite clock the extent of it. You&#10;&lt;em&gt;must&lt;/em&gt; carry cash if you want to be able to do pretty much anything - some&#10;places will take IC Card, or Apple Pay if you&amp;rsquo;re very lucky, but for the most&#10;part you&amp;rsquo;re paying for everything with cash.&lt;/p&gt;&#10;&lt;p&gt;There are ATMs in every conbini, though some charge different fees. I found the&#10;ones in 7-Elevens to be the cheapest (and 7-Eleven has the best taiyaki too).&lt;/p&gt;&#10;&lt;p&gt;When buying things in shops, it&amp;rsquo;s conventional to put cash on a little tray&#10;that&amp;rsquo;ll be on the counter - handing someone money directly is not really done.&#10;I&amp;rsquo;m not sure it&amp;rsquo;s considered rude, exactly, as cashiers will then often hand&#10;change back to you directly, but it&amp;rsquo;s something to keep in mind as I definitely&#10;mixed this up and caused brief bafflement.&lt;/p&gt;&#10;&lt;h2 id="places"&gt;Places&lt;/h2&gt;&#10;&lt;h3 id="nagoya"&gt;Nagoya&lt;/h3&gt;&#10;&lt;p&gt;I didn&amp;rsquo;t know much about Nagoya before we went, so I confess it wasn&amp;rsquo;t the place&#10;I was most excited about. Nagoya is lovely, it turns out. It proved to be the&#10;perfect gentle introduction to Japan as it&amp;rsquo;s comparatively quiet.&lt;/p&gt;&#10;&lt;p&gt;We were there for the F1, so didn&amp;rsquo;t spend a huge amount of time in the city, but&#10;we did eat a lot of great food. Nagoya has a couple of dishes it&amp;rsquo;s famous for&#10;and you must try them.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Hitsumabushi&lt;/strong&gt; is eel served with rice, and it&amp;rsquo;s delicious. Atsuta Horaiken&#10;claims to have invented it so try there if you have time, but we went to&#10;&lt;a href="https://maps.app.goo.gl/b3ynmfwkZD2EobD1A"&gt;Inou Hitsumabushi&lt;/a&gt; and it was&#10;wonderful.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2024/04/japan5-3.jpeg" alt="Hitsumabushi"&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Miso katsu&lt;/strong&gt; is miso pork cutlet. Chicken katsu curry is very popular in the&#10;UK so I was surprised to learn it&amp;rsquo;s not &lt;em&gt;really&lt;/em&gt; a thing in Japan - tonkatsu&#10;(pork) is the norm, and miso katsu is what happens when tonkatsu was a very good&#10;pig in its lifetime.&lt;br&gt;&#10;This is &lt;em&gt;the&lt;/em&gt; Nagoya dish and appropriately there&amp;rsquo;s a whole chain for it called&#10;&lt;a href="https://maps.app.goo.gl/tKvqG1P8WJS5ZEBo8"&gt;Misokatsu Yabaton&lt;/a&gt;. You might have&#10;to queue but it&amp;rsquo;s so worth it.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2024/04/japan6-2.jpeg" alt="Misokatsu Yabaton"&gt;&lt;/p&gt;&#10;&lt;h3 id="hiroshima-prefecture"&gt;Hiroshima prefecture&lt;/h3&gt;&#10;&lt;p&gt;We didn&amp;rsquo;t go to Hiroshima itself, but if you&amp;rsquo;re in the area I cannot strongly&#10;recommend Tadanoumi and Okunoshima enough.&lt;/p&gt;&#10;&lt;p&gt;Tadanoumi is a port town, and it was a wonderful way to experience a bit more of&#10;what small-town life in Japan is like. We had dinner at &lt;a href="https://maps.app.goo.gl/MobkobMDnUPx313x9"&gt;Okonomiyaki Fuji&lt;/a&gt;, where the food was delicious and the&#10;owner was so lovely; it was the first place my Japanese got a real workout, and&#10;where I felt like I got to meet some locals who weren&amp;rsquo;t so used to tourists.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2024/04/japan8-1.jpeg" alt="A shrine in Tadanoumi"&gt;&#10;&lt;img src="https://www.joelotter.com/img/2024/04/japan8-3.jpeg" alt="Okonomiyaki"&gt;&#10;&lt;img src="https://www.joelotter.com/img/2024/04/japan8-4.jpeg" alt="Lucy, her parents, and a friend we made in Tadanoumi"&gt;&lt;/p&gt;&#10;&lt;p&gt;The other big reason to visit Tadanoumi is that is hosts the ferry port to&#10;&lt;a href="http://rabbit-island.info/en/"&gt;Okunoshima, the Rabbit Island&lt;/a&gt;. This is an&#10;island with a population of hundreds of very tame wild rabbits. It&amp;rsquo;s&#10;incredible, and the island is so beautiful.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2024/04/japan9-1.jpeg" alt=""&gt;&#10;&lt;img src="https://www.joelotter.com/img/2024/04/japan9-4.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;The ferry over is very cheap, and there&amp;rsquo;s a little cafe at the hotel on the&#10;island if you want to get lunch there.&lt;/p&gt;&#10;&lt;p&gt;We stayed the night in Tadanoumi and went to the island the next day, and then&#10;on to Osaka in the evening, but I imagine it would be pretty easy to do as a day&#10;trip out of Hiroshima.&lt;/p&gt;&#10;&lt;h3 id="osaka"&gt;Osaka&lt;/h3&gt;&#10;&lt;p&gt;Osaka&amp;rsquo;s famous for food, and this is justified. We ate &lt;em&gt;extremely&lt;/em&gt; well here.&lt;/p&gt;&#10;&lt;p&gt;On the first night we had Yakitori at&#10;&lt;a href="https://maps.app.goo.gl/25neeS9KQQyJhevB7"&gt;uto&lt;/a&gt;. This is right by Shinsekai&#10;Market which features the &lt;a href="https://www.tsutenkaku.co.jp/"&gt;Tsutenkaku&lt;/a&gt; tower -&#10;it doesn&amp;rsquo;t cost much to go up and you get a pretty incredible view across the&#10;city. There is also a slide I regret not going on.&lt;/p&gt;&#10;&lt;p&gt;You&amp;rsquo;re definitely going to want to visit Dotenbori, and we found a couple of&#10;really good food places round there:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Okonomiyaki at &lt;a href="https://maps.app.goo.gl/7BpXNpA5YsYN4uWF6"&gt;Chibo&lt;/a&gt; - it&amp;rsquo;s&#10;touristy and you&amp;rsquo;ll have to queue but it&amp;rsquo;s worth it.&lt;/li&gt;&#10;&lt;li&gt;Soba at &lt;a href="https://maps.app.goo.gl/pAcpEmLhze5hUmXF6"&gt;Akari Soba&lt;/a&gt; - I had the&#10;duck one, it&amp;rsquo;s excellent. They do some local sakes too.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;We also had shabu-shabu, which I honestly found to be a bit tourist-baity and&#10;quite underwhelming, but it was good fun so give that a look if you think you&amp;rsquo;d&#10;enjoy it.&lt;/p&gt;&#10;&lt;p&gt;In addition, Dotenbori is just packed with street food stalls selling takoyaki&#10;or karaage, both of which are divine.&lt;/p&gt;&#10;&lt;p&gt;If you&amp;rsquo;re a fan of sweet breakfast, there are loads of places doing the Japanese&#10;souffle-style pancakes, we enjoyed&#10;&lt;a href="https://maps.app.goo.gl/1Sf6hNkwRsCTXnB7A"&gt;Elk&lt;/a&gt; and &lt;a href="https://maps.app.goo.gl/5cEr2PZ2QjWyYLrk9"&gt;Matsutake&lt;/a&gt;. The latter is right by Super Potato,&#10;a retro games store, which was one of my personal highlights.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2024/04/japan10-4.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Osaka Castle is also well worth a visit! If you decide to go up, don&amp;rsquo;t make the&#10;same mistake we did - start and the top and work your way down, that way the&#10;narrative of the exhibits will actually make sense.&lt;/p&gt;&#10;&lt;p&gt;We also loved &lt;a href="https://maps.app.goo.gl/Xk4HasqtE7hAoSzd9"&gt;Round One&lt;/a&gt;, an arcade&#10;with something like nine floors. A lot of the games are quite hard to parse if&#10;you&amp;rsquo;re a beginner or your Japanese isn&amp;rsquo;t good, but they&amp;rsquo;ve got Taiko no&#10;Tatsujin and two whole floors of UFO machines. I won nothing.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2024/04/japan12-3.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;h3 id="nara"&gt;Nara&lt;/h3&gt;&#10;&lt;p&gt;We did Nara as a day trip out of Osaka, but it&amp;rsquo;s quick to get to from most&#10;places and it&amp;rsquo;s absolutely stunning, perhaps my favourite day of the whole trip.&#10;Get some senbei for the deer and do the walk up to the shrines, you&amp;rsquo;ll have a&#10;wonderful time.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2024/04/japan13-2.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;h3 id="kyoto"&gt;Kyoto&lt;/h3&gt;&#10;&lt;p&gt;We only did a couple of days in Kyoto as we were sort of warned off it due to&#10;how busy it gets, but to be honest I could have done another couple of days.&lt;/p&gt;&#10;&lt;p&gt;The first place we went was &lt;a href="https://maps.app.goo.gl/fwWqYzFjhsyanbF79"&gt;Higashi Hongan-ji temple&lt;/a&gt;, which I absolutely loved.&#10;I found it deeply peaceful. It&amp;rsquo;s also one of the largest wooden buildings in the&#10;world.&lt;/p&gt;&#10;&lt;p&gt;That same day, we had the best ramen of my life at a place nearby called &lt;a href="https://maps.app.goo.gl/ZwMbCzEn8SxPhwLd8"&gt;Honke&#10;Daiichi-Asahi&lt;/a&gt;. You&amp;rsquo;ll have to queue,&#10;like all the best places in Japan, but it&amp;rsquo;s worth it. Both these places are&#10;really near Kyoto station too.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2024/04/14-japan-4.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;We stayed two nights outside Kyoto, in a ryokan (traditional inn) &lt;a href="https://maps.app.goo.gl/PnSSWTxgz114grBA7"&gt;in Ohara&lt;/a&gt;, with an onsen (natural hot&#10;spring) - it&amp;rsquo;s pretty affordable and really lovely.&lt;/p&gt;&#10;&lt;p&gt;On our second day in Kyoto we went to &lt;a href="https://maps.app.goo.gl/kZTp8eRksztrM7Vg6"&gt;Kiyomizu-dera&lt;/a&gt; and &lt;a href="https://maps.app.goo.gl/cNdER1Wd17kn2xHj6"&gt;Fushimi Inari&lt;/a&gt;, both of which I&amp;rsquo;d call must-sees.&#10;The latter gets &lt;em&gt;very&lt;/em&gt; busy but the higher up the mountain you go the fewer&#10;people there are.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2024/04/15-japan-2.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;On our last day, we walked along the &lt;a href="https://maps.app.goo.gl/Q3ozMDgwHJPK83SYA"&gt;Philosopher&amp;rsquo;s Path&lt;/a&gt; and had lunch at a cafe run by an&#10;elderly couple named &lt;a href="https://maps.app.goo.gl/ieabgU9LeDeYm9757"&gt;Rorian&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;We also went to Nishiki Market which is home to a very famous knife shop called&#10;Aritsugu.&lt;/p&gt;&#10;&lt;h3 id="tokyo"&gt;Tokyo&lt;/h3&gt;&#10;&lt;p&gt;Tokyo was our last stop, we spent about a week there. I expected to find it too&#10;urban, too overwhelming, too &lt;em&gt;big&lt;/em&gt;, but I actually ended up loving it. It&amp;rsquo;s huge&#10;but somehow manages to feel spacious in a way London never has.&lt;/p&gt;&#10;&lt;p&gt;We had lots of amazing food in Tokyo, so here are some highlights.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;I&amp;rsquo;d heard that Japan had a great burger culture, so we finally tried one at&#10;&lt;a href="https://maps.app.goo.gl/sz3bTRZQjNgTWdDX6"&gt;J.S. Burgers Cafe&lt;/a&gt; in Shinjuku. I&#10;had one called The Deep, which sounds like the stage name of a terrible spoken&#10;word artist but was, in fact, an incredible burger.&lt;/li&gt;&#10;&lt;li&gt;We got sushi and sashimi a few times. My advice would be to just go wherever,&#10;as even the cheapest sushi in Japan is going to be better than most places in&#10;the UK. The best I had was at Tsukiji Fish Market, though be warned that the&#10;actual fish market is now elsewhere and Tsukiji is now a slightly expensive&#10;tourist trap (it&amp;rsquo;s still great though, honestly).&#10;&lt;img src="https://www.joelotter.com/img/2024/04/17-japan2-3.jpeg" alt=""&gt;&lt;/li&gt;&#10;&lt;li&gt;A friend took us to an izakaya called &lt;a href="https://maps.app.goo.gl/giZ2voHJrvkQ6yqE8"&gt;Unosato&lt;/a&gt;, which was fantastic. Lots of small&#10;plates so you can try lots of things and really great sashimi.&lt;/li&gt;&#10;&lt;li&gt;The best coffee I had in Japan was at an unassuming little cafe in Asakusa&#10;called &lt;a href="https://maps.app.goo.gl/Ka2QtomRzM2Ko3BR9"&gt;MOCCA&lt;/a&gt;. It&amp;rsquo;s also rabbit&#10;themed, so Lucy was happy.&lt;/li&gt;&#10;&lt;li&gt;We had soba at &lt;a href="https://maps.app.goo.gl/5haRiqUaPj2M2twh9"&gt;Ginza Kagari&lt;/a&gt; -&#10;very different to soba I&amp;rsquo;ve had before as it was in quite a thick broth. Really&#10;good.&#10;&lt;img src="https://www.joelotter.com/img/2024/04/japan-last-day-2.jpeg" alt=""&gt;&lt;/li&gt;&#10;&lt;li&gt;As a treat at the end we had wagyu at a teppanyaki restaurant called &lt;a href="https://maps.app.goo.gl/9iwRZGn8CM3aPrmD9"&gt;Grow&lt;/a&gt; in Roppongi. Pricey, but really good.&#10;&lt;img src="https://www.joelotter.com/img/2024/04/japan-last-day-4.jpeg" alt=""&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;I&amp;rsquo;d say &lt;a href="https://maps.app.goo.gl/9ZHaAzKnmnDwyT6w8"&gt;Tokyo Sky Tree&lt;/a&gt; is a must -&#10;the &lt;a href="https://www.joelotter.com/notes/2024/04/19-japan/"&gt;views&lt;/a&gt; are incredible, and there&amp;rsquo;s a Pokémon&#10;Center in the mall at the bottom.&lt;/p&gt;&#10;&lt;p&gt;We had a &lt;a href="https://www.joelotter.com/notes/2024/04/19-japan-19/"&gt;night out&lt;/a&gt; in&#10;Kabukicho at the Golden Gai, the area with all the tiny bars. Folks have since&#10;told me it&amp;rsquo;s expensive and touristy, but the bar we were at was full of locals&#10;I had a nice time chatting to, so I think it depends on which one you get. Be&#10;aware most of the bars have a cover charge of about 1000 yen.&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://www.joelotter.com/notes/2024/04/20-japan-kamakura/"&gt;Kamakura&lt;/a&gt; is a day&#10;trip out of Tokyo, and it&amp;rsquo;s lovely. See the Great Buddha, then go to the beach.&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://maps.app.goo.gl/PtAY5bRJtiVQSwds9"&gt;Asakusa&lt;/a&gt; is a lovely older part of&#10;town with temples and shrines.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;That&amp;rsquo;s all I can extract from my brain right now - I&amp;rsquo;ll add to this list as I&#10;recall more!&lt;/p&gt;&#10;&lt;p&gt;I loved Japan. I can&amp;rsquo;t wait to go back. I think you&amp;rsquo;re going to love it too.&lt;/p&gt;&#10;</content:encoded></item><item><title>You gotta do your prerolls</title><link>https://www.joelotter.com/posts/2024/02/prerolls/</link><pubDate>Sat, 10 Feb 2024 13:33:16 +0000</pubDate><guid>https://www.joelotter.com/posts/2024/02/prerolls/</guid><description>&lt;p&gt;My pal Zach has this ritual when we play D&amp;amp;D - before we start, he rolls the&#10;D20. Over and over and over again.&lt;/p&gt;</description><content:encoded>&lt;p&gt;My pal Zach has this ritual when we play D&amp;amp;D - before we start, he rolls the&#10;D20. Over and over and over again.&lt;/p&gt;&#10;&lt;p&gt;The purpose of this, he says, is to &amp;ldquo;get all the bad rolls out&amp;rdquo;. A dice, you&#10;see, has a certain number of good and bad rolls within it (or are they within&#10;the roller?). If you roll the dice a bunch of times, you get all the crap out,&#10;so that when it comes time to roll for real you&amp;rsquo;ve got all that out of the way&#10;and can score crits. Or something.&lt;/p&gt;&#10;&lt;p&gt;Zach is the person on the left in this post&amp;rsquo;s header photo. Look how smug he&#10;looks. That&amp;rsquo;s a man who&amp;rsquo;s rolled out all his critical fails before we even&#10;start.&lt;/p&gt;&#10;&lt;p&gt;Anyway, we call it prerolls, and we all do it now. It&amp;rsquo;s become one of those&#10;rituals, like eating ill-advisedly large pizzas or blowing on the dice. It must&#10;work, because if it didn&amp;rsquo;t we would have stopped doing it.&lt;/p&gt;&#10;&lt;p&gt;The creative process works like this, too, a comparison that is absolutely not&#10;new or novel in any way. You have to make a load of shit before you can start&#10;making the good stuff. If you try and make the good stuff right away, it&amp;rsquo;ll be&#10;shit, and then you&amp;rsquo;ll get discouraged.&lt;/p&gt;&#10;&lt;p&gt;Human personification of the &amp;ldquo;mum says it&amp;rsquo;s my turn on the Xbox&amp;rdquo; meme Ed Sheeran&#10;compares it to &lt;a href="https://writingcooperative.com/the-dirty-faucet-method-why-you-need-to-write-more-bad-articles-6cdd8a9fd11e"&gt;a dirty faucet&lt;/a&gt;, which I think is lovely, actually.&lt;/p&gt;&#10;&lt;p&gt;My partner is an actual, proper, professional writer - a journalist - and she&#10;also loves writing short stories. But she never does it because, she says, she&amp;rsquo;s&#10;scared that she&amp;rsquo;ll sit down and write something and it&amp;rsquo;ll never be as good as it&#10;is in her head.&lt;/p&gt;&#10;&lt;p&gt;She&amp;rsquo;s correct, as she is about most things, but the thing is, it&amp;rsquo;ll be shit at&#10;first and then everything thereafter will be a little bit better. I don&amp;rsquo;t think,&#10;as artists, we ever get something to come out exactly as it is in our head.&#10;Usually it&amp;rsquo;s a little off. Sometimes, especially in my experience in poetry, it&#10;goes off in a completely different direction and you produce something you&amp;rsquo;re&#10;really proud of. Most of my poems I actually still like are ones where I&#10;intended to write something rather different.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve not written many poems recently - less because I&amp;rsquo;m scared of them being&#10;shit and more because the whole process feels deeply unpleasant of late - but&#10;one thing that was impressed into me through various workshops and collectives&#10;was the need to &lt;em&gt;play&lt;/em&gt;. A lot of the best art is created by just pissing about.&#10;Play doesn&amp;rsquo;t really have an outcome, almost by definition, so I always found&#10;these things hard to square with each other.&lt;/p&gt;&#10;&lt;p&gt;May god grant me the peace of mind to make shit art, and the self-awareness to&#10;not necessarily put it out in the world.&lt;/p&gt;</content:encoded></item><item><title>The games of 2023</title><link>https://www.joelotter.com/posts/2023/12/games-played-2023/</link><pubDate>Sun, 31 Dec 2023 14:29:24 +0000</pubDate><guid>https://www.joelotter.com/posts/2023/12/games-played-2023/</guid><description>&lt;p&gt;As many have written, 2023 was a great year for the games themselves and a&#10;pretty dire year for the industry and the people working in it. Let&amp;rsquo;s hope&#10;things improve in 2024.&lt;/p&gt;</description><content:encoded>&lt;p&gt;As many have written, 2023 was a great year for the games themselves and a&#10;pretty dire year for the industry and the people working in it. Let&amp;rsquo;s hope&#10;things improve in 2024.&lt;/p&gt;&#10;&lt;p&gt;I did a fair amount of game development in 2023, though it was less intensely&#10;focused than the previous year - I worked on &lt;a href="https://github.com/Bearwaves/growl"&gt;Growl&lt;/a&gt; a fair amount, but also went back to add a&#10;bunch of features to Feud. That game is now, finally, feature-complete, with a&#10;full release in early 2024. You heard it here first, probably.&lt;/p&gt;&#10;&lt;p&gt;A few stray observations about my game-playing habits this year:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;I got a Steam Deck in March and it&amp;rsquo;s rapidly become my favourite way to play&#10;games. If a game in this list says PC, I probably played it on the Deck.&lt;/li&gt;&#10;&lt;li&gt;I played very few iOS games this year - I really do think Apple Arcade has&#10;become enshittified, and I&amp;rsquo;m continuing to pay for it purely for Grindstone&#10;and Good Sudoku.&lt;/li&gt;&#10;&lt;li&gt;I mostly used the Xbox for small indies on Game Pass, the PS5 for the&#10;bigger-budget stuff that my PC can&amp;rsquo;t really handle these days, and the Switch&#10;as a Zelda machine. My days of defaulting to buying indies on Switch so I&#10;could play them on the go have been ended by the Deck.&lt;/li&gt;&#10;&lt;li&gt;My VR headset did not come out of the drawer for more than a couple of&#10;sessions the entire year.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;I played 40 different games for the first time in 2023, which is a fair bit more&#10;than last year but still some way off my game-playing heyday. As ever, this list&#10;doesn&amp;rsquo;t include anything I bounced off quickly or played for less than an hour,&#10;unless it&amp;rsquo;s a shorter-form game where that was the intention. It also doesn&amp;rsquo;t&#10;include anything I started in previous years, though I&amp;rsquo;ve popped an honourable&#10;mention section at the end.&lt;/p&gt;&#10;&lt;p&gt;You can see my lists from previous years here:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2022/12/games-played-2022"&gt;2022&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2021/12/games-played-2021"&gt;2021&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2020/12/games-played-2020"&gt;2020&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2019/12/games-played-2019"&gt;2019&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2018/12/games-played-2018"&gt;2018&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2017/12/games-played-2017"&gt;2017&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h4 id="roadwarden-pc"&gt;Roadwarden (PC)&lt;/h4&gt;&#10;&lt;p&gt;Roadwarden stresses me out with its tight time limit, but that&amp;rsquo;s very much the&#10;intended design. It pushes you to explore and experiment and take risks in a way&#10;I&amp;rsquo;m often averse to, which means you see much more of its wonderfully realised&#10;world. It&amp;rsquo;s a treat.&lt;/p&gt;&#10;&lt;h4 id="it-takes-two-xbox"&gt;It Takes Two (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;I played this with my partner who is very much Not Into Games and it was a good&#10;time! It&amp;rsquo;s beginner-friendly enough to work even for someone who&amp;rsquo;s not terribly&#10;comfortable with controlling a character in 3D, and the collaborative puzzles&#10;mean it never feels like one player is carrying the other (except when that&amp;rsquo;s an&#10;actual mechanic).&lt;/p&gt;&#10;&lt;h4 id="bonfire-peaks-pc"&gt;Bonfire Peaks (PC)&lt;/h4&gt;&#10;&lt;p&gt;This is an extremely clever puzzle game that I am extremely not clever enough&#10;for. I was able to make a bit more headway with it than I was Stephen&amp;rsquo;s Sausage&#10;Roll but this is one for the clever clogs. Clogses?&lt;/p&gt;&#10;&lt;h4 id="far-changing-tides-xbox"&gt;FAR: Changing Tides (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;I didn&amp;rsquo;t play the previous game in this series so I went in with very little&#10;idea what to expect. FAR is really lovely - it&amp;rsquo;s mechanically interesting&#10;throughout, with some lavish yet understated worldbuilding.&lt;/p&gt;&#10;&lt;h4 id="somerville-xbox"&gt;Somerville (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;I played two games this year from former Playdead developers. This is by some&#10;distance the less good one, but it&amp;rsquo;s still a good time and really visually&#10;striking. I didn&amp;rsquo;t get to the end of it but I do intend to go back (he says,&#10;knowing he probably never will).&lt;/p&gt;&#10;&lt;h4 id="dead-space-pc"&gt;Dead Space (PC)&lt;/h4&gt;&#10;&lt;p&gt;I am a Big Scaredy Cat and had therefore avoided the Dead Space series for&#10;years, but when this remake came out at the start of this year I thought I&amp;rsquo;d be&#10;a Big Brave Boy and try it out. It turns out Dead Space isn&amp;rsquo;t that scary, or at&#10;least not the kind of horror that really gets to me, so I had a pretty good time&#10;with it. I did find the combat a bit wearing after a while but there&amp;rsquo;s much more&#10;interesting sci-fi storytelling at play here than I ever expected, and the&#10;almost-Metroidvania-y structure really lends itself to this setting (I&amp;rsquo;m told&#10;the original was much more linear so this seems like a tangible improvement).&lt;/p&gt;&#10;&lt;h4 id="season-a-letter-to-the-future-pc"&gt;Season: A Letter to the Future (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2023/12/games-played-2023/season.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I think I&amp;rsquo;m the only person I know personally who liked this game, and I &lt;em&gt;loved&lt;/em&gt;&#10;it. There&amp;rsquo;s something about the writing style that really works for me in a way&#10;I&amp;rsquo;d not really seen done in games before - it&amp;rsquo;s loose, dreamy, dare I say&#10;&lt;em&gt;poetic&lt;/em&gt; - it&amp;rsquo;s a game written in the way I like to write. That aside, though,&#10;cycling through this gorgeous world is lovely enough to warrant giving it a try.&#10;I just wish they&amp;rsquo;d add an option to drop that &amp;lsquo;cinematic&amp;rsquo; letterboxing.&lt;/p&gt;&#10;&lt;h4 id="metroid-prime-remastered-switch"&gt;Metroid Prime Remastered (Switch)&lt;/h4&gt;&#10;&lt;p&gt;I played Metroid Prime on the original hardware not so long ago, and frankly&#10;this is a game that absolutely did not need a remaster, but being able to play&#10;one of the best games ever made on the plane with proper twin-stick controls is&#10;just dreamy.&lt;/p&gt;&#10;&lt;h4 id="destiny-2-lightfall-pc"&gt;Destiny 2: Lightfall (PC)&lt;/h4&gt;&#10;&lt;p&gt;After a really excellent year Destiny&amp;rsquo;s momentum and player goodwill came&#10;crashing back to earth with the launch of the worst expansion since maybe The&#10;Dark Below. There were bits I liked, and Strand is genuinely great, but the&#10;whole thing comes apart at the very obvious seams due to some really terrible&#10;writing and plot structure.&lt;/p&gt;&#10;&lt;p&gt;I do think the seasonal content this year has been quite good but this will be a&#10;hard one for Bungie to recover from, especially in the wake of their recent mass&#10;layoff. A shame.&lt;/p&gt;&#10;&lt;h4 id="desk-job-pc"&gt;Desk Job (PC)&lt;/h4&gt;&#10;&lt;p&gt;This is the free game you get to demo the Steam Deck hardware, but it&amp;rsquo;s well&#10;worth checking out - it&amp;rsquo;s really fun.&lt;/p&gt;&#10;&lt;h4 id="resident-evil-2-pc"&gt;Resident Evil 2 (PC)&lt;/h4&gt;&#10;&lt;p&gt;This was the first game where the Deck really wowed me - it runs perfectly. I&amp;rsquo;d&#10;not really played much Resident Evil ever, apart from 4 many years ago, and was&#10;really pleasantly surprised by how much it leans into some quite campy adventure&#10;game mechanics and aesthetics. It&amp;rsquo;s great.&lt;/p&gt;&#10;&lt;h4 id="dread-delusion-pc"&gt;Dread Delusion (PC)&lt;/h4&gt;&#10;&lt;p&gt;&amp;ldquo;What if Morrowind but in the style of a psychedelic rock album cover&amp;rdquo; seems to&#10;be what Dread Delusion&amp;rsquo;s going for and from what I played it absolutely hits&#10;that mark. I actually dipped after realising the main story content isn&amp;rsquo;t all in&#10;the game yet but I&amp;rsquo;ll be back once it leaves Early Access for sure.&lt;/p&gt;&#10;&lt;h4 id="cult-of-the-lamb-pc"&gt;Cult of the Lamb (PC)&lt;/h4&gt;&#10;&lt;p&gt;It&amp;rsquo;s kind of a mishmash of some slightly better games but the combination of the&#10;roguelite action levels and the colony management sim really does work. Lovely&#10;art, too.&lt;/p&gt;&#10;&lt;h4 id="zelda-tears-of-the-kingdom-switch"&gt;Zelda: Tears of the Kingdom (Switch)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2023/12/games-played-2023/totk.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;How do you make a sequel to the best game ever made? I think Nintendo could have&#10;decided to go bigger or to go deeper, and I&amp;rsquo;m so glad they mostly did the&#10;latter. Literally, in terms of the Depths and the verticality at play throughout&#10;the game, as well as mechanically; I can&amp;rsquo;t imagine playing the game without&#10;Ultrahand now, for all that when I saw the initial trailer I thought it was a&#10;gimmick.&lt;/p&gt;&#10;&lt;p&gt;I should have known better - in the hands of Nintendo&amp;rsquo;s finest minds, Tears of&#10;the Kingdom will probably keep me happily playing for the next six years.&#10;There&amp;rsquo;s a richness to it that makes re-exploring the same world almost as&#10;exciting as discovering it for the first time was.&lt;/p&gt;&#10;&lt;h4 id="rytmos-ios"&gt;Rytmos (iOS)&lt;/h4&gt;&#10;&lt;p&gt;It&amp;rsquo;s not &lt;em&gt;really&lt;/em&gt; a rhythm game, but it is a really excellent puzzle game in&#10;which rhythms emerge from finding solutions. I&amp;rsquo;ve also discovered a few&#10;previously-unknown genres of music and instruments. It&amp;rsquo;s great, get it on iOS,&#10;the PC version has wonky controls.&lt;/p&gt;&#10;&lt;h4 id="humanity-pc"&gt;Humanity (PC)&lt;/h4&gt;&#10;&lt;p&gt;At this point Enhance seems to be a studio that can do no wrong, and Humanity is&#10;as good as I&amp;rsquo;d hoped. It&amp;rsquo;s a gently forgiving puzzle game with a compelling&#10;aesthetic. It&amp;rsquo;s also the only thing that managed to get me to charge the VR&#10;headset this entire year - the VR mode is optional but it&amp;rsquo;s really quite nice,&#10;giving a sense of scale to the crowds that works really well.&lt;/p&gt;&#10;&lt;h4 id="street-fighter-6-pc"&gt;Street Fighter 6 (PC)&lt;/h4&gt;&#10;&lt;p&gt;Other than Super Smash Bros. in my uni days I am, and have always been, bad at&#10;fighting games. With SF6 launching I thought I&amp;rsquo;d make an effort to rectify that.&lt;/p&gt;&#10;&lt;p&gt;Did I become good at fighting games? No. But did I play online? Also no. I&#10;played the Yakuza-lite single-player mode and it&amp;rsquo;s great.&lt;/p&gt;&#10;&lt;h4 id="jett-given-time-ps5"&gt;JETT: Given Time (PS5)&lt;/h4&gt;&#10;&lt;p&gt;I really loved JETT a few years back, so I was quite excited about this free&#10;expansion. They seem to have gone for going harder on the systemic, ecology&#10;stuff that a lot of people felt the original didn&amp;rsquo;t make the most of. Sadly,&#10;it&amp;rsquo;s at the expense of the game&amp;rsquo;s character work and storytelling that this has&#10;been done, which is what so enamoured me at the time, so I didn&amp;rsquo;t enjoy this one&#10;quite as much.&lt;/p&gt;&#10;&lt;h4 id="final-fantasy-xvi-ps5"&gt;Final Fantasy XVI (PS5)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;ve not finished this yet but I&amp;rsquo;ve played quite a bit of it. I&amp;rsquo;m not sure how&#10;to feel quite yet. On the one hand it never quite feels good in the hands, the&#10;combat feels a little unresponsive and generally a bit clunky. The side quests&#10;are truly awful; so bad that the game actually has a different icon for the ones&#10;that matter. I don&amp;rsquo;t understand how a game that presumably started development&#10;in a post-Witcher 3 world could drop the ball so hard on this.&lt;/p&gt;&#10;&lt;p&gt;On the other hand, some of the writing is quite good, the world is interesting&#10;and I want to know more about it, and the voice acting is really genuinely&#10;excellent. The banter between characters has carried me through some of its&#10;dreariest sections.&lt;/p&gt;&#10;&lt;h4 id="south-scrimshaw-part-one-pc"&gt;South Scrimshaw, Part One (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2023/12/games-played-2023/scrimshaw.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I played this free visual novel in one train journey back from Liverpool and I&#10;haven&amp;rsquo;t been able to stop thinking about it since. It&amp;rsquo;s a sort of sci-fi nature&#10;documentary about a species of whale on an alien planet. The attention to&#10;detail, the evocative art and the implications that the writing drops about&#10;the wider setting are just spectacular. If you liked Scavengers Reign you&amp;rsquo;d love&#10;this. My game of the year.&lt;/p&gt;&#10;&lt;h4 id="eastward-pc"&gt;Eastward (PC)&lt;/h4&gt;&#10;&lt;p&gt;The art style drew me in to this one and largely kept me there. It&amp;rsquo;s a lovely&#10;thing - a little inconsistent and derivative in places, but I&amp;rsquo;ve been really&#10;enjoying my fairly limited time with it.&lt;/p&gt;&#10;&lt;h4 id="baldurs-gate-pc"&gt;Baldur’s Gate (PC)&lt;/h4&gt;&#10;&lt;p&gt;No, not that one. In a brief fit of madness I took it upon myself to not start&#10;Baldur&amp;rsquo;s Gate 3 until I&amp;rsquo;d played the previous games in the series. This year,&#10;I&amp;rsquo;ve put about 30 hours into the first one.&lt;/p&gt;&#10;&lt;p&gt;Baldur&amp;rsquo;s Gate is a curious thing. In many ways the game is very hard to play -&#10;difficulty spikes are frequent and unpredictable, it is &lt;em&gt;very&lt;/em&gt; heavily reliant&#10;on random numbers, and party members have a tendency to wander off and try to&#10;give bosses a hug.&lt;/p&gt;&#10;&lt;p&gt;However, even in this early title you can see the beginnings of what became the&#10;BioWare house style. It&amp;rsquo;s also a much funnier, sillier game than I had been led&#10;to believe. I&amp;rsquo;ll probably keep going with my ill-advised plan - while I can&amp;rsquo;t&#10;wait to get to 3 eventually, I&amp;rsquo;ve heard 2 is a banger.&lt;/p&gt;&#10;&lt;h4 id="the-outer-worlds-pc"&gt;The Outer Worlds (PC)&lt;/h4&gt;&#10;&lt;p&gt;In a year where we had a &amp;lsquo;Fallout in space&amp;rsquo; and it was a total flop, this one&#10;from a few years back holds up somewhat better.&lt;/p&gt;&#10;&lt;p&gt;I still bounced off it after the first act, though - the game plays a common but&#10;very annoying trick where it sets up a big moral decision, gives you some&#10;relatively clear clues as to the potential consequences of that decision, then&#10;throws a huge curveball at you after you&amp;rsquo;ve made it. &amp;ldquo;Siding with the oppressor&#10;is sometimes good, actually&amp;rdquo; is a take I saw Horizon Forbidden West make last&#10;year. I didn&amp;rsquo;t like it then and I don&amp;rsquo;t like it now. It&amp;rsquo;s not big and it&amp;rsquo;s not&#10;clever, stop it.&lt;/p&gt;&#10;&lt;h4 id="viewfinder-pc"&gt;Viewfinder (PC)&lt;/h4&gt;&#10;&lt;p&gt;I played this at WASD back in March after being enchanted by footage of its&#10;geometry-shifting mechanic and I&amp;rsquo;m happy to report that after launch it&amp;rsquo;s even&#10;better. The game is not content to simply rest on a killer hook, and plays with&#10;the central verb in some really fun ways.&lt;/p&gt;&#10;&lt;h4 id="twilight-princess-hd-wii-u"&gt;Twilight Princess HD (Wii U)&lt;/h4&gt;&#10;&lt;p&gt;I last played through Twilight Princess when I got it with my Wii, oh no, 17&#10;years ago. In my memory it was one of my favourite modern Zeldas, and while I&#10;think time has not necessarily been kind to its reputation I do think the game&#10;largely holds up. Zant is still really, really creepy, and the dark tone&#10;actually continues to work rather than sliding into teenage edgelord nonsense&#10;the way I would have assumed.&lt;/p&gt;&#10;&lt;h4 id="system-shock-pc"&gt;System Shock (PC)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;d never played the original and I&amp;rsquo;ve not got too far with this one yet, but&#10;can we just take a minute to talk about that art style? The low-res, pixelly&#10;texture work on top of modern 3D models is just a really sumptuous way to bring&#10;a game of this era forward without losing its feel. More of this!&lt;/p&gt;&#10;&lt;h4 id="armored-core-6-pc"&gt;Armored Core 6 (PC)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;d never played a game in this series before and I&amp;rsquo;m &lt;em&gt;very&lt;/em&gt; bad at it, but it&#10;really does feel incredible in the hands and there&amp;rsquo;s something about its tone&#10;I find extremely compelling. One to revisit.&lt;/p&gt;&#10;&lt;h4 id="chants-of-sennaar-pc"&gt;Chants of Sennaar (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2023/12/games-played-2023/sennaar.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Heaven&amp;rsquo;s Vault is a favourite of mine so I was very excited about another&#10;translation game. Sennaar didn&amp;rsquo;t disappoint; it&amp;rsquo;s a beautifully contained&#10;puzzle game, showing remarkable restraint and tying everything up with a rather&#10;satisfying ending. I even 100%&amp;rsquo;d this one, something I increasingly rarely do&#10;these days.&lt;/p&gt;&#10;&lt;h4 id="cocoon-xbox"&gt;COCOON (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2023/12/games-played-2023/cocoon.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Sometimes I play a game and wish I&amp;rsquo;d made it, sometimes I play a game and can&amp;rsquo;t&#10;imagine how anyone could have made it. COCOON is both of these things. It&amp;rsquo;s&#10;simple yet intricate, immediately understandable yet consistently delightful,&#10;and delivers one of the most creative mechanics I&amp;rsquo;ve yet seen. I played this&#10;the weekend I was home for my Granny&amp;rsquo;s funeral and it somehow did a lot to&#10;soothe me. Play it.&lt;/p&gt;&#10;&lt;h4 id="planet-of-lana-xbox"&gt;Planet of Lana (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;It was sold to me as a &amp;ldquo;Ghibli-esque platformer&amp;rdquo; and while I didn&amp;rsquo;t get far&#10;enough to see that trademark darkness start to creep through it is a really&#10;beautiful game, that does &lt;em&gt;just&lt;/em&gt; enough within the confines of what I feel is a&#10;rather tired genre to keep things interesting. That said, I don&amp;rsquo;t see myself&#10;going back to finish this one.&lt;/p&gt;&#10;&lt;h4 id="unravel-two-xbox"&gt;Unravel Two (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;I played the original game a few years back as a single-player experience and&#10;was distinctly underwhelmed. The addition of a second player is crucial; the&#10;mechanic where one person holds the yarn and the other swings becomes absolutely&#10;essential to most of the game&amp;rsquo;s best puzzles. It&amp;rsquo;s ace. I don&amp;rsquo;t understand how&#10;they missed it first time around but I&amp;rsquo;m very glad they were given another go.&lt;/p&gt;&#10;&lt;h4 id="jusant-xbox"&gt;Jusant (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;I assumed I&amp;rsquo;d spend my Christmas break finishing this one but I actually got&#10;sucked back into NieR Replicant so didn&amp;rsquo;t. Sorry, Jusant.&lt;/p&gt;&#10;&lt;p&gt;Anyway: it&amp;rsquo;s brilliant. I&amp;rsquo;m not much of a climber myself but even with my&#10;limited bouldering experience the mechanics of this game do a whole lot to&#10;capture the feel of a steady climb. That would almost be enough, but it adds in&#10;some wonderful visual and worldbuilding work. A treat. I love how Don&amp;rsquo;t Nod have&#10;become a studio that eschews &amp;ldquo;house style&amp;rdquo; to just make whatever the hell they&#10;want.&lt;/p&gt;&#10;&lt;h4 id="alan-wake-2-ps5"&gt;Alan Wake 2 (PS5)&lt;/h4&gt;&#10;&lt;p&gt;Alan Wake 2 is in so many ways a Joel Game, so I struggle to articulate exactly&#10;why I haven&amp;rsquo;t really been enjoying my time with it. It just feels a bit clunky&#10;and slow and keeps making me do rubbish combat. I think I&amp;rsquo;ll stick with it as I&#10;want to see where the story goes but I confess I&amp;rsquo;m disappointed by this one and&#10;feel like I&amp;rsquo;m in the minority.&lt;/p&gt;&#10;&lt;h4 id="the-talos-principle-pc"&gt;The Talos Principle (PC)&lt;/h4&gt;&#10;&lt;p&gt;This one feels like a puzzle game built out of the spare parts of a shooter, and&#10;that&amp;rsquo;s because it is. Somehow zooming around a puzzle game like it&amp;rsquo;s a Quake&#10;level actually really works, too.&lt;/p&gt;&#10;&lt;h4 id="a-highland-song-pc"&gt;A Highland Song (PC)&lt;/h4&gt;&#10;&lt;p&gt;I was fortunate enough to playtest this one from Inkle, one of my very&#10;favourite studios (I&amp;rsquo;m in the credits, mum!) back at the start of this year. I&#10;knew I&amp;rsquo;d love it so I didn&amp;rsquo;t let myself play again until it was properly&#10;released, and I&amp;rsquo;m glad I waited, it&amp;rsquo;s wonderful. My very favourite bits are when&#10;Moira is running downhill and you have to hit the button in time with some&#10;lively trad. No other game has come as close to capturing the feeling of going&#10;for a big walk in the mountains.&lt;/p&gt;&#10;&lt;h4 id="subpar-pool-ios"&gt;Subpar Pool (iOS)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2023/12/games-played-2023/subparpool.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I play this on iPad which is just the perfect platform for this kind of game. It&#10;takes a very simple mechanic - pool, with the rough edges sanded off - and then&#10;adds increasingly creative twists. My favourite so far is the modifier where&#10;holes move every time you pot something - it makes planning a shot much harder&#10;but if you actually manage to pull off a double-pot it feels incredible.&lt;/p&gt;&#10;&lt;h4 id="dave-the-diver-pc"&gt;Dave the Diver (PC)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;m not getting into the Game Awards debate over whether this counts as indie&#10;because I think the Game Awards are a waste of everyone&amp;rsquo;s time, but it &lt;em&gt;is&lt;/em&gt;&#10;extremely good. The writing is occasionally slightly wonky but somehow this only&#10;adds to its charm. My understanding is that I&amp;rsquo;ve barely scratched the surface of&#10;everything this game becomes. Runs great on the Deck, too.&lt;/p&gt;&#10;&lt;h4 id="wildermyth-pc"&gt;Wildermyth (PC)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;d been meaning to play Wildermyth for ages but had always been a bit nervous&#10;of it due to the nature of procedurally-generated storytelling; I just hadn&amp;rsquo;t&#10;really seen it done well. Past tense. Wildermyth is very special. I&amp;rsquo;ve only done&#10;the first bit of it but I&amp;rsquo;m already very attached to these characters.&lt;/p&gt;&#10;&lt;h4 id="before-the-green-moon-pc"&gt;Before The Green Moon (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2023/12/games-played-2023/greenmoon.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I bought this one knowing nothing about it, and not even as a fan of the genre,&#10;purely because of how much I loved this studio&amp;rsquo;s previous game, Wide Ocean Big&#10;Jacket. It&amp;rsquo;s a very different thing, but shares that game&amp;rsquo;s understated yet&#10;extremely sharp character writing and sense of place. I can&amp;rsquo;t comment on whether&#10;this will measure up to the expectations of someone expecting a Harvest Moon or&#10;Stardew Valley - I suspect it won&amp;rsquo;t come close - but as an eight-hour story&#10;about community, found family and work it&amp;rsquo;s really effective and, to me, quite&#10;affecting.&lt;/p&gt;&#10;&lt;h4 id="super-mario-wonder-switch"&gt;Super Mario Wonder (Switch)&lt;/h4&gt;&#10;&lt;p&gt;I never really got on with the New Super Mario Bros. series - something about&#10;the controls just didn&amp;rsquo;t really work for me - but with this one they&amp;rsquo;ve&#10;absolutely nailed analog stick controls for a 2D platformer. It feels&#10;incredible, it&amp;rsquo;s joyous, it&amp;rsquo;s colourful, it made me grin like a fool. It&amp;rsquo;s&#10;also, in some places, really hard. All stripes of Mario players should be happy&#10;with this one, though if that&amp;rsquo;s you I imagine you&amp;rsquo;ve played it already.&lt;/p&gt;&#10;&lt;h4 id="honourable-mentions"&gt;Honourable mentions&lt;/h4&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;I technically started Signalis last year and didn&amp;rsquo;t get too far wth it, but&#10;this year I decided it was time to give it the attention it deserves, and I&amp;rsquo;m&#10;very glad I did. My recommendation would be to finish it, form your own&#10;opinions on what the hell happens in this game, then go and watch Sam Greer&amp;rsquo;s&#10;&lt;a href="https://youtu.be/SUnVUXAn-fQ?si=apt9t93pwx3T9Adg"&gt;really excellent video&lt;/a&gt;.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;I got back into Divinity Original Sin 2 &lt;em&gt;again&lt;/em&gt;. Still haven&amp;rsquo;t finished it,&#10;still having a blast.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;I still can&amp;rsquo;t beat Hades. I&amp;rsquo;m bad at games.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;Have a lovely 2024. Maybe next year&amp;rsquo;s post will include Baldur&amp;rsquo;s Gate 3 (but I&#10;wouldn&amp;rsquo;t hold my breath).&lt;/p&gt;</content:encoded></item><item><title>Bluesky backfeed support in Bridgy</title><link>https://www.joelotter.com/posts/2023/10/bridgy-bluesky/</link><pubDate>Tue, 31 Oct 2023 22:26:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2023/10/bridgy-bluesky/</guid><description>&lt;p&gt;My &amp;ldquo;weekend project&amp;rdquo; of implementing Bluesky support into &lt;a href="https://brid.gy/about"&gt;Bridgy&lt;/a&gt; is finally, several months later, complete! Ish.&lt;/p&gt;</description><content:encoded>&lt;p&gt;My &amp;ldquo;weekend project&amp;rdquo; of implementing Bluesky support into &lt;a href="https://brid.gy/about"&gt;Bridgy&lt;/a&gt; is finally, several months later, complete! Ish.&lt;/p&gt;&#10;&lt;p&gt;Bridgy is an important part of the &lt;a href="https://www.joelotter.com/posts/2023/03/indieweb/"&gt;IndieWeb&lt;/a&gt;; it is&#10;responsible for ensuring that responses to posts on social networks like&#10;Mastodon get backfed so they show up on the original posts on this website.&lt;/p&gt;&#10;&lt;p&gt;Like many people, I recently started using Bluesky. Bridgy&amp;rsquo;s maintainer,&#10;&lt;a href="https://snarfed.org"&gt;Ryan&lt;/a&gt;, has been working on supporting the AT Protocol,&#10;Bluesky&amp;rsquo;s underlying protocol, in Bridgy Fed for some time. I took it upon&#10;myself to add Bluesky support to the vanilla Bridgy.&lt;/p&gt;&#10;&lt;p&gt;At the time of writing, we&amp;rsquo;ve shipped backfeeding! That means Bridgy can send&#10;webmentions in response to replies, likes and reposts on Bluesky. Publishing -&#10;POSSEing things over to Bluesky from your website - will come later.&lt;/p&gt;&#10;&lt;p&gt;You can try it for yourself! Head to &lt;a href="https://brid.gy/bluesky/start"&gt;Bridgy&lt;/a&gt; to&#10;get started. In the meantime, here is a short list of thoughts I had about the&#10;development process.&lt;/p&gt;&#10;&lt;h3 id="python-isgood-maybe"&gt;Python is&amp;hellip;good? Maybe?&lt;/h3&gt;&#10;&lt;p&gt;I hadn&amp;rsquo;t &lt;em&gt;really&lt;/em&gt; touched Python &amp;ldquo;in anger&amp;rdquo; for quite a few years. We use it in&#10;some legacy systems at Emitwise, but I haven&amp;rsquo;t gone too deep into the depths of&#10;those myself.&lt;/p&gt;&#10;&lt;p&gt;My impression of Python from my limited exposure to it since the days when it&#10;was my main language was that its lack of static typing made programming in it&#10;quite sloppy, overreliant on external tools and editor plugins to ensure the&#10;code you&amp;rsquo;re writing makes basic sense.&lt;/p&gt;&#10;&lt;p&gt;I still &lt;em&gt;kind of&lt;/em&gt; think that&amp;rsquo;s true. However, working on Bridgy has reminded me&#10;of a few things about the language I really like:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Inbuilt debugger support just by adding a &lt;code&gt;breakpoint()&lt;/code&gt; statement&lt;/li&gt;&#10;&lt;li&gt;Decorators are extremely cool&lt;/li&gt;&#10;&lt;li&gt;Generators and list comprehensions, ditto&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;and, of course, one thing I&amp;rsquo;d forgotten how much I hate:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Pip.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;I enjoyed dipping my toes back into Python, and it&amp;rsquo;s been great practice for&#10;when I inevitably have to use it professionally again, but I must confess I&#10;found myself missing Go.&lt;/p&gt;&#10;&lt;h3 id="the-at-protocol-is-extremely-well-thought-out"&gt;The AT Protocol is extremely well thought-out&lt;/h3&gt;&#10;&lt;p&gt;I run a Mastodon &lt;a href="https://otter.garden"&gt;instance&lt;/a&gt;, and I&amp;rsquo;m kind of inclined to&#10;root (toot) for Mastodon in the battle to succeed the Bird Website. With that&#10;being said, working with the AT Protocol has been really lovely. I think it has&#10;a couple of key advantages over ActivityPub:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Using DNS as the source-of-truth to look up a user&amp;rsquo;s DID (identifier) is&#10;really smart.&lt;/li&gt;&#10;&lt;li&gt;The repository-based structure means some of the big, difficult problems&#10;Mastodon is currently facing can hopefully be solved in a structured way, in&#10;particular account migration between instances.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Of course, the proof will be when federation actually happens.&lt;/p&gt;&#10;&lt;h3 id="next-bits"&gt;Next bits&lt;/h3&gt;&#10;&lt;p&gt;I&amp;rsquo;ve got a few bugs to iron out, but the next big thing in this process is&#10;publishing. I&amp;rsquo;m personally very excited to have this done, as it means I&amp;rsquo;ll be&#10;able to fully automate the POSSE stuff for this website.&lt;/p&gt;&#10;&lt;p&gt;To close, a shoutout to Ryan Barrett, for being the model of what a good&#10;open-source maintainer looks like: friendly, encouraging, knowledgable and&#10;responsive.&lt;/p&gt;</content:encoded></item><item><title>Using Vim for writing</title><link>https://www.joelotter.com/posts/2023/10/vim-writing/</link><pubDate>Tue, 24 Oct 2023 15:11:56 +0100</pubDate><guid>https://www.joelotter.com/posts/2023/10/vim-writing/</guid><description>&lt;p&gt;I do most of my daily work in Vim, and by this point I find it quite hard to use&#10;anything else, but for writing that isn&amp;rsquo;t code (or, let&amp;rsquo;s be real, mostly YAML&#10;these days) it could be a lot better.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I do most of my daily work in Vim, and by this point I find it quite hard to use&#10;anything else, but for writing that isn&amp;rsquo;t code (or, let&amp;rsquo;s be real, mostly YAML&#10;these days) it could be a lot better.&lt;/p&gt;&#10;&lt;p&gt;A good writing environment &lt;em&gt;should&lt;/em&gt; free me from worrying unnecessarily about&#10;Computer Things like line numbers, line wrapping and file formats. I should be&#10;able to just type words and not think about the other stuff - at least, until&#10;after I&amp;rsquo;m finished.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve used a few tools in the past that went some way towards this - Sublime Text&#10;has quite a nice &lt;a href="https://www.sublimetext.com/docs/distraction_free.html"&gt;distraction-free writing mode&lt;/a&gt;, which centres text and&#10;removes all other UI elements. It&amp;rsquo;s neat, but I don&amp;rsquo;t really like Sublime that&#10;much these days.&lt;/p&gt;&#10;&lt;p&gt;I wanted to take some of those elements and transplant them into Vim.&#10;Thankfully, as is often the way, someone&amp;rsquo;s done a plugin. Two, in this case.&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://github.com/junegunn/goyo.vim"&gt;goyo.vim&lt;/a&gt; provides a distraction-free&#10;writing environment much like the one in Sublime. It centres the text, limits it&#10;to a small width so you don&amp;rsquo;t have to scan horizontally, and removes all other&#10;distractions from the UI. It&amp;rsquo;s great.&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://github.com/junegunn/limelight.vim"&gt;limelight.vim&lt;/a&gt; is something of a&#10;companion to Goyo - it dims all text other than the bit you&amp;rsquo;re focused on. It&amp;rsquo;s&#10;even smart enough to distinguish paragraphs regardless of how your line breaking&#10;works in your file, which I&amp;rsquo;ll get to in a minute.&lt;/p&gt;&#10;&lt;p&gt;You can install these two in the usual way and they make a big difference just&#10;by themselves. It looks like the image at the top of this post, which I think is&#10;great. I&amp;rsquo;ve made a few additions. Here&amp;rsquo;s the relevant bit of my &lt;code&gt;.vimrc&lt;/code&gt; (I&amp;rsquo;ll&#10;explain it, don&amp;rsquo;t worry):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-vimrc" data-lang="vimrc"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;noremap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Leader&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;g&lt;/span&gt; :&lt;span class="nx"&gt;Goyo&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CR&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;#34;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;colorcolumn&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;81&lt;/span&gt; &lt;span class="c"&gt;&amp;#34;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;scrolloff&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="c"&gt;&amp;#34;3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;g&lt;/span&gt;:&lt;span class="nx"&gt;goyo_width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;81&lt;/span&gt; &lt;span class="c"&gt;&amp;#34;4&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;:&lt;span class="nx"&gt;goyo_enter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;&amp;#34;5&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;scrolloff&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;999&lt;/span&gt; &lt;span class="c"&gt;&amp;#34;6&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;linebreak&lt;/span&gt; &lt;span class="c"&gt;&amp;#34;7&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;textwidth&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;80&lt;/span&gt; &lt;span class="c"&gt;&amp;#34;8&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;CocDisable&lt;/span&gt; &lt;span class="c"&gt;&amp;#34;9&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Limelight&lt;/span&gt; &lt;span class="c"&gt;&amp;#34;10&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;endfunction&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;:&lt;span class="nx"&gt;goyo_leave&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;&amp;#34;11&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;scrolloff&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;nolinebreak&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;textwidth&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;CocEnable&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Limelight&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;endfunction&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;autocmd&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="nx"&gt;GoyoEnter&lt;/span&gt; &lt;span class="nx"&gt;nested&lt;/span&gt; &lt;span class="nx"&gt;call&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SID&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;goyo_enter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;&amp;#34;12&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;autocmd&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="nx"&gt;GoyoLeave&lt;/span&gt; &lt;span class="nx"&gt;nested&lt;/span&gt; &lt;span class="nx"&gt;call&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SID&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;goyo_leave&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;noremap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Leader&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; :&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;spell&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt; &lt;span class="nx"&gt;spelllang&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;en_gb&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CR&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;#34;13&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s go through the numbered lines:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;This toggles my &amp;ldquo;writing mode&amp;rdquo; (using goyo.vim) on Leader+G. I have my leader&#10;key mapped to the space bar - if you&amp;rsquo;ve not remapped your leader key it&amp;rsquo;s&#10;probably my number one recommendation for getting productive with Vim.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;This adds a column marker at column 81 (so max width 80) when &lt;em&gt;not&lt;/em&gt; in the&#10;writing mode. You can see what it looks like here:&lt;/p&gt;&#10;&lt;a href="https://www.joelotter.com/posts/2023/10/vim-writing/"&gt;&#10; &lt;img src="vim.jpg" alt="" title="" /&gt;&#10; &lt;/a&gt;&#10;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;In Vim, &lt;code&gt;scrolloff&lt;/code&gt; refers to how far away from the screen edge your cursor&#10;needs to be before it starts scrolling. I have mine set to 5 as a reasonable&#10;default; this is very much personal preference.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;This setting tells goyo.vim how many columns on your screen to take up. The&#10;default is 80, though I found this actually matches up to a max width of 79,&#10;which isn&amp;rsquo;t quite what I want.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;The next chunk, inside &lt;code&gt;goyo_enter&lt;/code&gt;, are the things that get run when&#10;goyo.vim is turned on. I think of this as &amp;ldquo;writing-mode settings&amp;rdquo;.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Setting &lt;code&gt;scrolloff=999&lt;/code&gt; basically ensures that, while in writing mode, the&#10;cursor stays in the middle of the screen; I find this, along with&#10;limelight.vim, helps me focus on the paragraph I&amp;rsquo;m currently working on.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Turn &lt;code&gt;linebreak&lt;/code&gt; on. This makes sure that goyo.vim (or Vim generally) doesn&amp;rsquo;t&#10;break lines in the middle of words.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Setting &lt;code&gt;textwidth=80&lt;/code&gt; forces Vim to auto-format my writing such that it&#10;stays within the 80-character limit. I only really want this on when I&amp;rsquo;m&#10;writing - for code I do sometimes need to go over that limit (Java is a&#10;verbose language).&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Turns off &lt;code&gt;coc.nvim&lt;/code&gt;, a plugin I use for code completion. This prevents the&#10;suggestions popup from appearing, which is almost never helpful when writing&#10;prose.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Turns limelight.vim on.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;This function, &lt;code&gt;goyo_leave&lt;/code&gt;, acts as the inverse of the other function,&#10;putting all my settings back to normal when leaving writing mode.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;These lines connect up Goyo&amp;rsquo;s events to the functions we just defined.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;A bonus one - this toggles spellcheck off and on using Leader+S.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;As always, all this is available in my public &lt;a href="https://github.com/joelotter/dotfiles"&gt;dotfiles&lt;/a&gt;. I wrote this post using my new writing mode and it genuinely did make a big&#10;difference.&lt;/p&gt;</content:encoded></item><item><title>Turns out immersive theatre isn’t video games</title><link>https://www.joelotter.com/posts/2023/08/punchdrunk/</link><pubDate>Sat, 19 Aug 2023 22:12:43 +0100</pubDate><guid>https://www.joelotter.com/posts/2023/08/punchdrunk/</guid><description>&lt;p&gt;I went to see Punchdrunk’s The Burnt City the other night.&#10;In the bar on the way in, I confidently said to my partner, &amp;ldquo;immersive&#10;theatre is video games&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;It isn&amp;rsquo;t.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I went to see Punchdrunk’s The Burnt City the other night.&#10;In the bar on the way in, I confidently said to my partner, &amp;ldquo;immersive&#10;theatre is video games&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;It isn&amp;rsquo;t.&lt;/p&gt;&#10;&lt;p&gt;If you&amp;rsquo;ve not heard of Punchdrunk I urge you to go and find out (and ideally buy&#10;a ticket if you&amp;rsquo;re in London). They&amp;rsquo;re the foremost purveyors of high-budget&#10;immersive theatre and I&amp;rsquo;d wanted to see one of their shows for years.&lt;/p&gt;&#10;&lt;p&gt;The Burnt City is a reinterpretation-slash-remix of Agamemnon and Hecuba.&#10;It takes place in&#10;a giant warehouse that&amp;rsquo;s been converted into a city, and the story unfolds&#10;in real time in several places at once. As a spectator you can follow a&#10;character of your choosing, stay in one place to observe what happens there, or&#10;just go exploring - I did a bit of all three.&lt;/p&gt;&#10;&lt;p&gt;Spectators are given white masks&#10;to wear and are forbidden from speaking, so it&amp;rsquo;s clear who&amp;rsquo;s involved and who&#10;isn&amp;rsquo;t, and those same spectators end up forming a backdrop that makes the city&#10;feel even more alive on the eve of its destruction. The story takes place over a&#10;one-hour-ish loop, so you have the opportunity to catch a couple of different&#10;perspectives. It&amp;rsquo;s breathtaking in its scope and ambition and it&amp;rsquo;s brilliant.&lt;/p&gt;&#10;&lt;p&gt;On the face of it, so far so video games, or so was my assumption. As I moved&#10;around the space, though, I was struck by how unlike anything I&amp;rsquo;ve played it is.&#10;The answer&amp;rsquo;s probably obvious - games, even those sometimes unfairly derided as&#10;&amp;ldquo;walking simulators&amp;rdquo;, give the participants agency in a way that would utterly&#10;break this kind of production. The whole point is that the spectator is a&#10;ghost - they can observe events, but they cannot change them, and the events&#10;will continue happening regardless of whether the spectator is even there.&lt;/p&gt;&#10;&lt;p&gt;This gets played with by Punchdrunk in lots of different ways. Characters will&#10;sometimes appear aware of the spectators and sometimes will not. A character&#10;you&amp;rsquo;re following might suddenly take off at a run, leading to an eerily silent&#10;stampede as they&amp;rsquo;re tailed by a mob of phantoms. They might even push past you&#10;or scream in your face - but crucially there&amp;rsquo;s nothing a participant can do, at&#10;least if they&amp;rsquo;re behaving themselves, that can upset this loop.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve been wondering whether this kind of interaction, or lack thereof, could&#10;work in a game. My gut feeling is that the inability for players to influence&#10;events in any way, even as a collective, would kill it. There are a couple of&#10;games, on reflection, that come close to the feeling Punchdrunk provides, but&#10;they&amp;rsquo;re not what I expected.&lt;/p&gt;&#10;&lt;p&gt;The game I&amp;rsquo;ve always had in my head as being the most immersive-theatre-y is&#10;Fullbright&amp;rsquo;s Tacoma, a game set on a seemingly derelict space station where what&#10;you&amp;rsquo;re mostly doing is rewatching conversations to try and piece together what&#10;happened. It&amp;rsquo;s fairly passive as these things go, but even then as a player&#10;you&amp;rsquo;re still making decisions - what order to watch things in, rewinding and&#10;pausing, gathering clues from recordings to unlock doors. The game is entirely&#10;centred around the player; there&amp;rsquo;s no sense that things would continue to&#10;unfold if you closed it.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2023/08/punchdrunk/hitman.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I think IO Interactive&amp;rsquo;s Hitman series hews far closer to what&#10;Punchdrunk are doing, at least up to a point. Each level is a real-time&#10;simulation, with events that will play out regardless of whether you&amp;rsquo;re watching&#10;them. You can - and I frequently do - play through a level without &lt;em&gt;doing&lt;/em&gt;&#10;anything, just watching, making notes. Who talks to who? Where do they go?&lt;/p&gt;&#10;&lt;p&gt;Unfortunately, playing the game like this will result in a fail state. Hitman is&#10;absolutely designed to be played this way, but there still by necessity has to&#10;come a point where you stick a very large bald spanner in the works in&#10;&lt;em&gt;exactly&lt;/em&gt; the right place and watch the whole delicate structure collapse around&#10;you. It&amp;rsquo;s exhilarating. It also ceases to be immersive theatre.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2023/08/punchdrunk/outerwilds.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Outer Wilds is one of the best video games ever made, and it&amp;rsquo;s another example&#10;of the player&amp;rsquo;s agency being used in an interesting way. In Outer Wilds, the&#10;sun explodes every twenty minutes, and nothing you do in those twenty minutes&#10;has any impact beyond that timeframe. You spend most of your time in the game&#10;exploring, gaining knowledge of this delightful clockwork solar system, learning&#10;how it all fits together. This lack of agency is broken in only two places,&#10;which I won&amp;rsquo;t spoil. You should play Outer Wilds.&lt;/p&gt;&#10;&lt;p&gt;I am &lt;em&gt;not&lt;/em&gt; going to talk about BioShock here, but if that&amp;rsquo;s in your head, good&#10;for you.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;d be interested to see a game studio make something that works in the way a&#10;Punchdrunk play does. I&amp;rsquo;m aware Punchdrunk have done a small amount of work in&#10;the space, and they&amp;rsquo;re doing &lt;em&gt;something&lt;/em&gt; with Niantic, so I&amp;rsquo;ll keep an eye on&#10;that. In the meantime, if you know of anything I ought to look at, holler.&lt;/p&gt;</content:encoded></item><item><title>On POSSE and IndieWeb</title><link>https://www.joelotter.com/posts/2023/03/indieweb/</link><pubDate>Fri, 10 Mar 2023 17:43:55 +0000</pubDate><guid>https://www.joelotter.com/posts/2023/03/indieweb/</guid><description>&lt;p&gt;I&amp;rsquo;ve gone down into the depths these last couple of weeks with IndieWeb,&#10;POSSE and a whole bunch of other arcane terminology. Now that I&amp;rsquo;ve come up for&#10;air, let&amp;rsquo;s chat about it.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I&amp;rsquo;ve gone down into the depths these last couple of weeks with IndieWeb,&#10;POSSE and a whole bunch of other arcane terminology. Now that I&amp;rsquo;ve come up for&#10;air, let&amp;rsquo;s chat about it.&lt;/p&gt;&#10;&lt;h2 id="what"&gt;What?&lt;/h2&gt;&#10;&lt;p&gt;The &lt;a href="https://indieweb.org/"&gt;IndieWeb organisation&lt;/a&gt; describe it like this:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;The IndieWeb is a community of independent &amp;amp; personal websites connected by&#10;simple standards, based on the principles of: owning your domain &amp;amp; using it&#10;as your primary identity, publishing on your own site (optionally&#10;syndicating elsewhere), and owning your data.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;What this actually means in practice, at least for myself, is the following:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;This website serves as my one canonical identity on the web.&lt;/li&gt;&#10;&lt;li&gt;Everything gets posted on this website first, and other places (Twitter,&#10;Mastodon, etc., often referred to as &lt;strong&gt;silos&lt;/strong&gt;) second. This includes blog&#10;posts but also other shortform posts, comparable to tweets/toots, that I&amp;rsquo;ll&#10;refer to as &amp;rsquo;notes'.&lt;/li&gt;&#10;&lt;li&gt;Any responses to posts or notes get fed back to here.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="why"&gt;Why?&lt;/h2&gt;&#10;&lt;p&gt;If the recent Twitter debacle has taught us anything it is that platforms aren&amp;rsquo;t&#10;stable. Tying your online identity to a corporate-owned website is fine for a&#10;while but in the very long run anything posted there is, ultimately, ephemeral.&lt;/p&gt;&#10;&lt;p&gt;Twitter might, genuinely, disappear in the next few years. Meta-owned platforms&#10;are slowly rotting as the algorithmic chum piles up - I need to rescue my photo&#10;albums before it&amp;rsquo;s too late. All the stuff I posted to Bebo as a teenager is&#10;irretrievably gone, and while in my personal case this is probably a&#10;good thing, the fragility of our online ecosystem is cause for concern.&lt;/p&gt;&#10;&lt;p&gt;Even Mastodon suffers from this - while the network itself cannot be&#10;meaningfully disrupted by, say, a fash-adjacent billionaire, an individual&#10;account might disappear due to a malicious or incompetent instance owner. I run&#10;&lt;a href="https://otter.garden/"&gt;my own instance&lt;/a&gt;, so this is less of a cause for concern&#10;for me, but I&amp;rsquo;m still uneasy about it.&lt;/p&gt;&#10;&lt;p&gt;The only thing we can really rely on on the web is the web itself. It is&#10;ubiquitous enough to be &lt;em&gt;mostly&lt;/em&gt; decentralised, has backwards-compatible&#10;standards that are &lt;em&gt;mostly&lt;/em&gt; agreed-upon, and is &lt;em&gt;mostly&lt;/em&gt; resilient to attack by&#10;anything other than, like, a nation, or that guy who broke BGP that one time.&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s still totally cool and desirable to use platforms - that&amp;rsquo;s where my friends&#10;mostly are, after all - but the idea of having my content live here, in a place&#10;I fully control, was something I was quite attracted to. Enter IndieWeb, which&#10;I first heard about through &lt;a href="https://jacky.wtf"&gt;Jacky Alciné&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h2 id="how"&gt;How?&lt;/h2&gt;&#10;&lt;p&gt;I&amp;rsquo;m not going to try and write a tutorial for getting set up on the IndieWeb&#10;because frankly I think it is quite a convoluted process and also because better&#10;resources than me exist. I started out with&#10;&lt;a href="https://indiewebify.me/"&gt;IndieWebify.Me&lt;/a&gt;, which is a fantastic resource for&#10;learning about what it &lt;em&gt;is&lt;/em&gt; as well as the various bits you need to implement.&lt;/p&gt;&#10;&lt;p&gt;Some blog platforms, like WordPress, make this process much simpler, but as this&#10;website is built as a static site there is some more work to do.&lt;/p&gt;&#10;&lt;h3 id="web-sign-in"&gt;Web Sign In&lt;/h3&gt;&#10;&lt;p&gt;I like this one a lot. The idea is that if your website is your canonical&#10;identity on the web, then it should be the authoritative source for how you log&#10;into stuff.&lt;/p&gt;&#10;&lt;p&gt;For example, I have various social links on my page - either in the sidebar or&#10;footer depending on how you&amp;rsquo;re reading this - like Twitter, Mastodon and GitHub.&#10;By adding a &lt;code&gt;rel=me&lt;/code&gt; tag to each of them, I inform any IndieWeb-compatible&#10;parsers that these links are to my various identities across the web. Some of&#10;those identities are on websites that offer a sign-in flow. This means I can log&#10;in to anything that supports Web Sign In by just providing my domain name.&lt;/p&gt;&#10;&lt;p&gt;Here&amp;rsquo;s what my links look like:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fab fa-github&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;me&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://github.com/JoelOtter&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fab fa-itch-io&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;me&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://itch.io/joelotter&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fab fa-mastodon&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;me&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://otter.garden/@joel&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fab fa-twitter&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;me&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://twitter.com/JoelOtter&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If I log into Webmention.io, a service we&amp;rsquo;ll be talking about in a minute, I get&#10;this:&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2023/03/indieweb/login.png" alt="A screenshot of IndieAuth"&gt;&lt;/p&gt;&#10;&lt;p&gt;There are various services implementing web sign-in, including IndieAuth and&#10;RelMeAuth. The IndieWeb wiki has a &lt;a href="https://indieweb.org/Web_sign-in"&gt;page&lt;/a&gt; on&#10;web sign-in, as it does for most things.&lt;/p&gt;&#10;&lt;h3 id="microformats"&gt;Microformats&lt;/h3&gt;&#10;&lt;p&gt;How do IndieWeb websites talk to each other? They read HTML! In order to tell&#10;them exactly how to parse your website, there&amp;rsquo;s a standard called&#10;&lt;a href="https://microformats.org/"&gt;Microformats&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m not going to go into the weeds with how exactly Microformats is used -&#10;again, there are better resources than me - but here are the broad steps I took&#10;to make my website IndieWeb compatible.&lt;/p&gt;&#10;&lt;p&gt;First up, you need a &lt;code&gt;h-card&lt;/code&gt; entry describing you and your website. This is&#10;just some basic info - you can think of it as a &amp;lsquo;profile&amp;rsquo;. Mine looks like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;h-card&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;u-url&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;me&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://www.joelotter.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;joelotter.com&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;p-name&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;me&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Joel Auterson&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;p-nickname&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;JoelOtter&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;u-photo&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/img/profile.jpg&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Joel Auterson&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;p-locality&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;London&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next up is ensuring your posts are marked up accordingly. I used the various&#10;validator tools on IndieWebify.me to get this right. Here&amp;rsquo;s a small sample (it&amp;rsquo;s&#10;a Go HTML template, hence the weird squiggles).&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;hidden&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;u-url&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .Permalink }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;time&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;dt-published&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Date.Format &amp;#34;2006-01-02T15:04:05Z0700&amp;#34; }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;time&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;author&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;p-author h-card&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .Site.BaseURL }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Joel Auterson&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;u-photo&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/img/profile.jpg&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Joel Auterson&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;p-summary&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ partial &amp;#34;plaintext_summary.html&amp;#34; . }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I also added &lt;code&gt;h-feed&lt;/code&gt; tags to anything that can be parsed as a feed, like the&#10;list of latest posts on the home page. I used the Microformats parser at&#10;&lt;a href="http://pin13.net/mf2/?url=https%3A%2F%2Fwww.joelotter.com"&gt;pin13.net&lt;/a&gt; to check&#10;I had everything working right.&lt;/p&gt;&#10;&lt;h3 id="posse"&gt;POSSE&lt;/h3&gt;&#10;&lt;p&gt;This is the big one, and probably the bit it took me the longest to get to where&#10;I want. POSSE stands for &amp;ldquo;Post on your Own Site, Syndicate Elsewhere&amp;rdquo;, and it&amp;rsquo;s&#10;how we get from posting on a website to posting on a website and having it go&#10;on my Twitter, Mastodon, whatever. There&amp;rsquo;s also PESOS (you work it out).&lt;/p&gt;&#10;&lt;p&gt;To get this working I use a service called &lt;a href="https://brid.gy/"&gt;brid.gy&lt;/a&gt;, which is&#10;amazing - essentially it parses your website and works out how to format a tweet&#10;or toot. It can also do what&amp;rsquo;s called &amp;ldquo;backfeeding&amp;rdquo;, which we&amp;rsquo;ll look at in a&#10;minute.&lt;/p&gt;&#10;&lt;p&gt;brid.gy has really good and comprehensive docs which I recommend. My flow for&#10;publishing something now goes like this:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Post it on my website&lt;/li&gt;&#10;&lt;li&gt;Wait for it to go online via GitHub Pages&lt;/li&gt;&#10;&lt;li&gt;Tell brid.gy about the post, and it&amp;rsquo;ll publish it to Twitter and Mastodon for&#10;me.&lt;/li&gt;&#10;&lt;li&gt;Update the post with the Twitter and Mastodon links.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;I&amp;rsquo;ve got my &lt;a href="https://www.joelotter.com/notes"&gt;notes&lt;/a&gt; set up to link to their syndicated posts. For a&#10;basic example, here is a &lt;a href="https://www.joelotter.com/notes/2023/03/05-asmr/"&gt;note&lt;/a&gt; from last weekend, and&#10;its corresponding &lt;a href="https://otter.garden/@joel/109972449640316818"&gt;Mastodon&lt;/a&gt; and&#10;&lt;a href="https://twitter.com/JoelOtter/status/1632471109000888321"&gt;Twitter&lt;/a&gt; posts.&lt;/p&gt;&#10;&lt;p&gt;brid.gy is also smart enough to work out what kind of thing it needs to publish&#10;to various networks. For example, &lt;a href="https://www.joelotter.com/notes/2023/03/07-jobs/"&gt;this note&lt;/a&gt; appears&#10;on my site as some text and a link, but thanks to the &lt;code&gt;u-repost-of&lt;/code&gt; tag brid.gy&#10;instead retweeted/boosted the original poster. You can do stuff like replies and&#10;quote-tweets too.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve seen some people that run their entire social media from just their&#10;website, even doing replies and so on. I haven&amp;rsquo;t quite got the patience for this&#10;due to the static nature of my website and the relative effort it takes to post&#10;something, but there are standards out there like&#10;&lt;a href="https://indieweb.org/Micropub"&gt;Micropub&lt;/a&gt; that I could try and implement&#10;against if I was so inclined.&lt;/p&gt;&#10;&lt;h3 id="backfeeding"&gt;Backfeeding&lt;/h3&gt;&#10;&lt;p&gt;You&amp;rsquo;ll notice that on my notes and posts you can see comments, likes and&#10;reposts. My blog doesn&amp;rsquo;t have a comment form - all of these come from Twitter&#10;and Mastodon, and are &lt;strong&gt;backfed&lt;/strong&gt; to my blog by brid.gy, via a Webmention.&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://indieweb.org/Webmention"&gt;Webmentions&lt;/a&gt; are an open standard for the web,&#10;and basically are like a sort of cross-site @mention. If you&amp;rsquo;re old enough to&#10;remember pingbacks in the blogosphere days, they&amp;rsquo;re a sort of non-shit version&#10;of that.&lt;/p&gt;&#10;&lt;p&gt;To send webmentions, parse a website&amp;rsquo;s HTML and look for its &lt;code&gt;rel=&amp;quot;webmention&amp;quot;&lt;/code&gt;&#10;link. I have one! It looks like this.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;webmention&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://webmention.io/www.joelotter.com/webmention&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I don&amp;rsquo;t currently have an automated setup for sending Webmentions. Instead, I&#10;pop my URL into a tool like &lt;a href="https://telegraph.p3k.io/"&gt;Telegraph&lt;/a&gt;, which looks&#10;for Webmention-compatible links on a page and sends them for you. This is the&#10;kind of thing I&amp;rsquo;ll probably set up a GitHub Action to automate on publish.&lt;/p&gt;&#10;&lt;p&gt;Receiving Webmentions is a little more complicated. Normally you&amp;rsquo;d have a server&#10;endpoint to receive them at, but I don&amp;rsquo;t have a server as I&amp;rsquo;m a static site.&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://webmention.io"&gt;Webmention.io&lt;/a&gt; solves this problem nicely. It acts as a&#10;public bucket for Webmentions that you can query from your site in JavaScript,&#10;then turn that into some layout. I&amp;rsquo;m not going to show you all my JS code for&#10;that because it&amp;rsquo;s gross, but here&amp;rsquo;s a little bit showing the relevant part.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://webmention.io/api/mentions?token={{ .Site.Params.webmentions.token }}&amp;amp;target=&amp;#34;&lt;/span&gt; &lt;span class="o"&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;Permalink&lt;/span&gt; &lt;span class="p"&gt;}}).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jsonData&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;jsonData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;links&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;interactionsDiv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;interactions&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;repost&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;createInteractionGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interactionsDiv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;reposts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;fa-retweet&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;like&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;createInteractionGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interactionsDiv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;likes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;like&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;fa-heart&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;reply&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;createReplyGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interactionsDiv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="whats-next"&gt;What&amp;rsquo;s next&lt;/h2&gt;&#10;&lt;p&gt;I&amp;rsquo;ve got to a point with this stuff where I&amp;rsquo;m pretty happy with it! The only&#10;thing I&amp;rsquo;d really like to add is some kind of Micropub-compatible setup where I&#10;could post notes as easily as I do through a Mastodon app, rather than needing&#10;to use Git on my computer.&lt;/p&gt;&#10;&lt;p&gt;This post makes IndieWeb stuff look really hard, but I think the majority of the&#10;difficulty is self-imposed by me sticking with a static site generator. If you&#10;were to run your website on WordPress or something there are plugins to do&#10;pretty much all of this for you.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve largely avoided Twitter since Space Karen took over, but this gives me&#10;quite a nice way to interact with it while keeping it at arm&amp;rsquo;s length. I don&amp;rsquo;t&#10;know how long this will last given the changes to their API.&lt;/p&gt;&#10;&lt;h2 id="further-reading"&gt;Further reading&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;The &lt;a href="https://indieweb.org/"&gt;IndieWeb wiki&lt;/a&gt; is a really fantastic resource&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://indiewebify.me/"&gt;IndieWebify.Me&lt;/a&gt; is invaluable for getting set up&lt;/li&gt;&#10;&lt;li&gt;I used &lt;a href="https://chringel.dev/2022/07/indiewebify-me-and-dont-forget-my-webmentions/"&gt;this blog post&lt;/a&gt;&#10;by Christian Engel for reference a lot during my journey through the IndieWeb&#10;warren. I&amp;rsquo;ve kind of inadvertently ended up replicating it. It&amp;rsquo;s also a great website to reference for formatting and so on!&lt;/li&gt;&#10;&lt;/ul&gt;</content:encoded></item><item><title>The Good Shit Pizza Manifesto</title><link>https://www.joelotter.com/posts/2023/03/good-shit-pizza/</link><pubDate>Mon, 06 Mar 2023 19:13:30 +0000</pubDate><guid>https://www.joelotter.com/posts/2023/03/good-shit-pizza/</guid><description>&lt;p&gt;I moved from East to South London in the summer of last year. South London has&#10;been amazing, especially for food, but there&amp;rsquo;s one thing missing.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I moved from East to South London in the summer of last year. South London has&#10;been amazing, especially for food, but there&amp;rsquo;s one thing missing.&lt;/p&gt;&#10;&lt;p&gt;I live in Dulwich-ish, and this means I have access to a huge number of amazing&#10;local food places in Peckham and Camberwell. Here is a quick list of some of&#10;my favourites.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="http://zeretkitchen.co.uk/"&gt;Zeret Kitchen&lt;/a&gt; - incredible Ethiopian food, get&#10;the big sharing dish.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.tripadvisor.co.uk/Restaurant_Review-g186338-d10488152-Reviews-Yada_s_Green_Kitchen-London_England.html"&gt;Yada&amp;rsquo;s Green Kitchen&lt;/a&gt; - Kurdish, amazing with a group of people so you can try everything.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.instagram.com/deserted_cactus/?hl=en"&gt;Deserted Cactus&lt;/a&gt; - this has become a Saturday lunchtime staple.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="http://www.madeofdough.co.uk/"&gt;Made of Dough&lt;/a&gt; - wood fired pizza, maybe the&#10;best in the area.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;It&amp;rsquo;s that last one that gives me pause. When I left Stepney I fully anticipated&#10;that I&amp;rsquo;d miss Efes and Tayyabs - both East End institutions that you must try! -&#10;but the thing I missed most was something of a surprise.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/posts/2023/03/good-shit-pizza/marios.jpg" alt="Mario’s Pizza, Limehouse"&gt;&lt;/p&gt;&#10;&lt;p&gt;South London is an embarrassment of riches when it comes to pizza. We have, to&#10;name just a few, Made of Dough, Voodoo Ray&amp;rsquo;s, Yard Sale and Theo&amp;rsquo;s. All of these&#10;are excellent pizza. They&amp;rsquo;re made with fresh dough and ingredients, they&amp;rsquo;re not&#10;greasy, you don&amp;rsquo;t feel gross after you eat them. They are Good Pizza.&lt;/p&gt;&#10;&lt;p&gt;Mario&amp;rsquo;s Pizza does not make Good Pizza. Mario&amp;rsquo;s Pizza is not made with fresh,&#10;healthy ingredients. Mario&amp;rsquo;s makes pizzas that are inch-thick discuses of fat&#10;and carbohydrates. Your arteries go &lt;em&gt;clang&lt;/em&gt; just from looking at them. You need&#10;a good lie down after eating one. They are Shit Pizza.&lt;/p&gt;&#10;&lt;p&gt;I miss them like hell.&lt;/p&gt;&#10;&lt;p&gt;Mario&amp;rsquo;s is the platonic example of Good Shit Pizza, a staple of my diet that I&#10;have been missing for far too long. I&amp;rsquo;ve now lived in each of London&amp;rsquo;s cardinal&#10;directions, and in each of them I&amp;rsquo;ve found a Good Shit Pizza place to call my&#10;own. In West London it was Pizza Pronto in West Kensington. In North, City Pizza&#10;on Holloway Road. In East, of course, it was Mario. From some cursory Googling&#10;all of these places are still open, so clearly there are others in London with&#10;the same discerning taste as myself.&lt;/p&gt;&#10;&lt;p&gt;I want to write a manifesto for Good Shit Pizza, to differentiate it from Good&#10;Pizza (as previously described), from Bad Shit Pizza (Tops, most kebab shop&#10;pizzas) and from plain old Bad Pizza, that which barely merits the name&#10;(Dominoes, Papa Johns, etc).&lt;/p&gt;&#10;&lt;p&gt;A Good Shit Pizza &lt;em&gt;must&lt;/em&gt;:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Have enough cheese to kill a small dog.&lt;/li&gt;&#10;&lt;li&gt;Be a bit trashy but not mega greasy.&lt;/li&gt;&#10;&lt;li&gt;Require a nap after consuming.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;A Good Shit Pizza &lt;em&gt;should&lt;/em&gt;:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Be deep pan, or at least offer it as an option.&lt;/li&gt;&#10;&lt;li&gt;Be reheatable the next day and taste perfectly fine.&lt;/li&gt;&#10;&lt;li&gt;Come with a garlic and herb dip.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;A Good Shit Pizza &lt;em&gt;must not&lt;/em&gt;:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Be anything a self-respecting Italian would put in their mouth.&lt;/li&gt;&#10;&lt;li&gt;Include chicken. We still have standards.&lt;/li&gt;&#10;&lt;li&gt;Have any tomato sauce visible; cheese coverage should be 100% at minimum.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;I write this as a manifesto, as a statement, but also as a plea. South London,&#10;I beg you: provide me with this morsel, this one little thing. A Good Shit Pizza&#10;would make my life complete.&lt;/p&gt;</content:encoded></item><item><title>The games of 2022</title><link>https://www.joelotter.com/posts/2022/12/games-played-2022/</link><pubDate>Fri, 30 Dec 2022 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2022/12/games-played-2022/</guid><description>&lt;p&gt;I had a really great year in 2022. I also played fewer games in 2022 than maybe&#10;any year previous. I don&amp;rsquo;t necessarily want to think about what that might&#10;mean, thank you very much.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I had a really great year in 2022. I also played fewer games in 2022 than maybe&#10;any year previous. I don&amp;rsquo;t necessarily want to think about what that might&#10;mean, thank you very much.&lt;/p&gt;&#10;&lt;p&gt;2022 was a year in which I formally left the games industry (I now work in&#10;climate which is super cool if often mildly depressing). It&amp;rsquo;s also, conversely,&#10;a year in which I learned more about game development than possibly ever&#10;before, due to my ongoing work on my game engine project, &lt;a href="https://github.com/Bearwaves/growl"&gt;Growl&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;I did play 25 games for the first time this year. As ever, this&#10;list doesn&amp;rsquo;t include anything I bounced off immediately or played for less than&#10;an hour or so (unless it was short). It also doesn&amp;rsquo;t include anything I had&#10;already started playing prior to 2022, though for this year I&amp;rsquo;ve opted to put&#10;a little &amp;lsquo;honourable mentions&amp;rsquo; bit at the end, just because.&lt;/p&gt;&#10;&lt;p&gt;You can see my lists from previous years here:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2021/12/games-played-2021"&gt;2021&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2020/12/games-played-2020"&gt;2020&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2019/12/games-played-2019"&gt;2019&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2018/12/games-played-2018"&gt;2018&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2017/12/games-played-2017"&gt;2017&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;The games of 2022 were as follows.&lt;/p&gt;&#10;&lt;h4 id="steinsgate-ps-vita"&gt;Steins;Gate (PS Vita)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;m still not 100% sold on Steins;Gate. I watched the anime a while back but&#10;didn&amp;rsquo;t make it to the end - the plot is really compelling but some of the&#10;character work really did not do it for me.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m delighted(?) to report, then, that the anime is in fact an extremely&#10;faithful adaptation of the source material. The visual novel format does&#10;somehow make the writing&amp;rsquo;s worst excesses more bearable, so I&amp;rsquo;ve got a bit&#10;further through it this time around.&lt;/p&gt;&#10;&lt;h4 id="psychonauts-2-xbox"&gt;Psychonauts 2 (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;Psychonauts 2 is the perfect way to do a sequel to a game that came out nearly&#10;20 years ago. It looks like you remember Psychonauts looking, but with all the&#10;bells and whistles our modern GPUs can produce. The movement is snappy, the&#10;humour is the same blend of irreverent and sincere that so endeared me to the&#10;original, and the level design is extraordinary. I haven&amp;rsquo;t finished it yet but&#10;I really do intend to.&lt;/p&gt;&#10;&lt;h4 id="guardians-of-the-galaxy-xbox"&gt;Guardians of the Galaxy (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;This game is pretty badly in need of a patch. Audio cutouts&#10;are extremely frequent, and I&amp;rsquo;d have missed huge portions of the game&amp;rsquo;s dialog&#10;if I wasn&amp;rsquo;t one of those dreadful people who insist on having subtitles on at&#10;all times.&lt;/p&gt;&#10;&lt;p&gt;Nevertheless, this game features the most likeable rendition of these&#10;characters I&amp;rsquo;ve seen on screen. The combat is snappy if not particularly&#10;original, but the story is genuinely a really fun adventure. If you&amp;rsquo;re a Marvel&#10;type you should check this out. If like me you aren&amp;rsquo;t a Marvel type, it might&#10;still be worth a look.&lt;/p&gt;&#10;&lt;h4 id="horizon-forbidden-west-ps5"&gt;Horizon Forbidden West (PS5)&lt;/h4&gt;&#10;&lt;p&gt;I played Horizon Zero Dawn very slowly over a period of several years, and the&#10;game was somehow stronger for it. HZD was a very typical open-world game that&#10;launched alongside a game that upended the whole notion of open-world, but it&#10;was one with fairly interesting worldbuilding, a compelling mystery to unravel,&#10;and was refreshingly free of bloat.&lt;/p&gt;&#10;&lt;p&gt;Horizon Forbidden West, unfortunately, shares few of these qualities - the one&#10;mystery the game does set up gets unraveled very quickly and is obvious from the&#10;outset, there&amp;rsquo;s far more Ubisoft-style map clutter than I&amp;rsquo;d prefer and Aloy is&#10;so unlikeable as a character that I can&amp;rsquo;t tell if it&amp;rsquo;s deliberate or just bad&#10;writing. Don&amp;rsquo;t even get me started on the union-busting quest in the first area.&lt;/p&gt;&#10;&lt;p&gt;With that being said, credit where credit is due to Guerilla&amp;rsquo;s engineering team,&#10;who have absolutely knocked it out of the park with a stunning technical&#10;achievement. I spent a good couple of hours at the start just wandering around,&#10;&lt;em&gt;looking&lt;/em&gt;.&lt;/p&gt;&#10;&lt;h4 id="destiny-2-the-witch-queen-pc"&gt;Destiny 2: The Witch Queen (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2022/12/destiny.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ll just say the same thing I always tell people when they ask about Destiny:&#10;the game is probably the best it&amp;rsquo;s ever been, the storytelling is fantastic, I&#10;&lt;em&gt;love&lt;/em&gt; that they roll the story out in little weekly bursts now so there&amp;rsquo;s&#10;always something to log in for - it&amp;rsquo;s also totally impenetrable to a newcomer in&#10;plot terms, requiring spending probably &lt;em&gt;days&lt;/em&gt; trawling through lore videos on&#10;YouTube in order to fully appreciate what&amp;rsquo;s happening in-game.&lt;/p&gt;&#10;&lt;p&gt;Bungie have gone all-in on rewarding their most dedicated players, and to folks&#10;like me that&amp;rsquo;s wonderful, but it&amp;rsquo;s a very hard game to recommend.&lt;/p&gt;&#10;&lt;p&gt;The Witch Queen is one of the best FPS campaigns I&amp;rsquo;ve played since Titanfall 2.&lt;/p&gt;&#10;&lt;h4 id="elden-ring-ps5"&gt;Elden Ring (PS5)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2022/12/elden-ring.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Look, you&amp;rsquo;ve heard all about Elden Ring. You&amp;rsquo;ve probably heard that it&amp;rsquo;s&#10;From Software&amp;rsquo;s most essential game, a game that exceeded even the massive&#10;expectations put upon it, a work of tremendous maximalism and, paradoxically,&#10;restraint. All of this is true.&lt;/p&gt;&#10;&lt;p&gt;Elden Ring is, for me, the game that learned the right lessons from Breath of&#10;the Wild, in the same way as Hollow Knight learned the right lessons from Dark&#10;Souls a few years previously. The two games couldn&amp;rsquo;t be more different in tone,&#10;setting, even mechanics, but that sense of &lt;em&gt;adventure&lt;/em&gt; they grant is unlike&#10;anything else. It would have been my game of the year if not for a tiny lil fox&#10;guy.&lt;/p&gt;&#10;&lt;h4 id="tunic-xbox"&gt;Tunic (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2022/12/tunic.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Tunic is, at first glance, a very pretty isometric tribute to the 2D Zelda&#10;games. If you were to engage with it purely on these terms I think you&amp;rsquo;d have&#10;a really great time. The combat is snappy and satisfyingly challenging, the&#10;world is a joy to explore and the plot, mostly hinted at, is compelling.&lt;/p&gt;&#10;&lt;p&gt;However, it&amp;rsquo;s the addition of the layer of puzzle mechanics on top of all this&#10;that solidifies Tunic as my game of the year. It&amp;rsquo;s hard to talk about without&#10;spoiling it, but essentially you are collecting pages of the game&amp;rsquo;s manual -&#10;rendered in loving detail and looking very much like real scans - through which&#10;additional mechanics, puzzle solutions and one huge overarching puzzle are&#10;hinted at. There&amp;rsquo;s a moment where something clicks and your whole perception&#10;of the game world changes, and that&amp;rsquo;s a rare and wonderful thing.&lt;/p&gt;&#10;&lt;h4 id="pokémon-gold-gbc"&gt;Pokémon Gold (GBC)&lt;/h4&gt;&#10;&lt;p&gt;I probably spent more time with the Analogue Pocket this year than I did the&#10;Switch; it really is a wonderful machine. I had some fun going into CeX stores&#10;to buy and repair old Game Boy games, and Pokémon Gold was one of my first&#10;victims.&lt;/p&gt;&#10;&lt;p&gt;I played Silver a &lt;em&gt;very&lt;/em&gt; long time ago, long enough that I didn&amp;rsquo;t remember much&#10;at all. Recent Pokémon games have failed to really capture my attention,&#10;largely due to a perceived sameyness and the relative lack of difficulty.&#10;Perhaps it&amp;rsquo;s just because I&amp;rsquo;m older, I thought.&lt;/p&gt;&#10;&lt;p&gt;Pokémon Gold is tough as nails. I&amp;rsquo;ve wiped out several times in the 10 or so&#10;hours I&amp;rsquo;ve played. Pokémon &lt;em&gt;was&lt;/em&gt; as tough as we remember it being, the games&#10;have genuinely got easier.&lt;/p&gt;&#10;&lt;h4 id="knotwords-ios"&gt;Knotwords (iOS)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2022/12/knotwords.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;The Wordle bug came for me this year just as much as anyone else, to the point&#10;where only once a day was just not enough. Enter the brilliantly-timed&#10;Knotwords, a game by Zach Gage and Jack Schlesigner, designers of the wonderful&#10;Good Sudoku.&lt;/p&gt;&#10;&lt;p&gt;Knotwords combines the best bits of Wordle with the best bits of crosswords and&#10;the wonderful design language of the creators&amp;rsquo; previous game. I think you can&#10;play it for free, too. No reason not to give it a shot.&lt;/p&gt;&#10;&lt;h4 id="ghostwire-tokyo-ps5"&gt;Ghostwire Tokyo (PS5)&lt;/h4&gt;&#10;&lt;p&gt;I feel like Ghostwire Tokyo got massively overshadowed by the stuff it launched&#10;alongside, and that&amp;rsquo;s not necessarily unfair. However, it &lt;em&gt;is&lt;/em&gt; a decent open&#10;world action game with a super cool setting and lovely visuals, and it&amp;rsquo;s worth&#10;looking at even if it appeals as nothing more than a super-detailed Tokyo to&#10;run around in.&lt;/p&gt;&#10;&lt;h4 id="olliolli-world-xbox"&gt;OlliOlli World (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;d not played the other games in this series but after seeing rave reviews I&#10;was encouraged to give the latest entry a try. I&amp;rsquo;m glad I did - OlliOlli World&#10;is joyous. I&amp;rsquo;m not anywhere close to finishing it, but it&amp;rsquo;s a fantastic thing&#10;to dip into when I&amp;rsquo;m tired or a bit down.&lt;/p&gt;&#10;&lt;h4 id="the-looker-pc"&gt;The Looker (PC)&lt;/h4&gt;&#10;&lt;p&gt;The Looker is a skewering of The Witness, a game I really like made by a person&#10;I don&amp;rsquo;t. The Looker separates itself from the dross of other &amp;lsquo;parody&amp;rsquo; games by&#10;being actually quite a good puzzle game in its own right, and by being really,&#10;genuinely funny.&lt;/p&gt;&#10;&lt;h4 id="stray-ps5"&gt;Stray (PS5)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2022/12/stray.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I played Stray with my partner, who is very much not into video games (we&amp;rsquo;re&#10;working on it) and we both had a great time. You get to be a cat! There&amp;rsquo;s a&#10;button just for going meow! You can knock things of ledges to be an utter&#10;bastard just like a real cat!&lt;/p&gt;&#10;&lt;p&gt;There&amp;rsquo;s a plot here which is quite nice if largely ignorable - the real joy&#10;is in being a cat (as mentioned) and exploring the detailed and often beautiful&#10;world the developers have created.&lt;/p&gt;&#10;&lt;h4 id="xenoblade-chronicles-wii3ds"&gt;Xenoblade Chronicles (Wii/3DS)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;d wanted to check this one out for many years, and the release of the very&#10;well-regarded Xenoblade Chronicles 3 gave me the push I needed to go back to&#10;the original.&lt;/p&gt;&#10;&lt;p&gt;I tried this out on the Wii first, and found it very engaging but just couldn&amp;rsquo;t&#10;get over the extremely awkward camera controls. I don&amp;rsquo;t play a lot of Wii these&#10;days but I don&amp;rsquo;t remember being desperate for a second analog stick when I did.&lt;/p&gt;&#10;&lt;p&gt;I managed to snag a copy of the 3DS edition of the game in CeX, and found it a&#10;bit easier to play, but the huge world and cluttered UI don&amp;rsquo;t lend themselves&#10;to the 3DS&amp;rsquo;s wee screen. The &amp;lsquo;definitive edition&amp;rsquo; on Switch doesn&amp;rsquo;t appeal&#10;either, as the art style has changed to more closely match the later games&amp;rsquo;&#10;anime-adjacent visuals, rather than the Vagrant Story look of the original.&lt;/p&gt;&#10;&lt;p&gt;I may load this one up on a 3DS emulator to get the best of both worlds.&lt;/p&gt;&#10;&lt;h4 id="links-awakening-game-boy"&gt;Link&amp;rsquo;s Awakening (Game Boy)&lt;/h4&gt;&#10;&lt;p&gt;I played the Switch version of Link&amp;rsquo;s Awakening when it launched and very much&#10;enjoyed it - there&amp;rsquo;s a playfulness to it that really holds up. The original&#10;Game Boy release has all of this - it really is just the same game. Even the&#10;Goombas.&lt;/p&gt;&#10;&lt;h4 id="wonder-boy-3-master-system"&gt;Wonder Boy 3 (Master System)&lt;/h4&gt;&#10;&lt;p&gt;OK there&amp;rsquo;s definitely a theme for this year, and it&amp;rsquo;s &amp;ldquo;going back to originals&#10;having played remasters and discovering how good the originals were in the&#10;first place&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;This is absolutely the case with Wonder Boy 3, which was remade into The&#10;Dragon&amp;rsquo;s Trap, which I adored. The tough-as-nails platforming, the intricate&#10;world and even the music were all present in the original (though I will say&#10;that I prefer the remastered audio, please don&amp;rsquo;t shout at me retro nerd guys).&lt;/p&gt;&#10;&lt;h4 id="papers-please-pc"&gt;Papers, Please (PC)&lt;/h4&gt;&#10;&lt;p&gt;The issue with Lucas Pope games is that every time I sit down to play one I get&#10;inspired within about ten minutes and go off to do some game development. So it&#10;is with Papers, Please, which is a fantastic game I haven&amp;rsquo;t put nearly enough&#10;time into to form a coherent opinion.&lt;/p&gt;&#10;&lt;h4 id="xcom-2-pc"&gt;XCOM 2 (PC)&lt;/h4&gt;&#10;&lt;p&gt;I really enjoy turn-based tactics games but somehow never had got to the&#10;supposed king of this genre. I like it! I did all the XCOM things, like get too&#10;attached to a soldier then immediately get them horribly murdered with my&#10;stupidity, or get angry at the hit probabilities for &lt;em&gt;clearly&lt;/em&gt; being a liar.&lt;/p&gt;&#10;&lt;p&gt;I also didn&amp;rsquo;t finish it, like every other turn based tactics game.&lt;/p&gt;&#10;&lt;h4 id="vampire-survivors-pc"&gt;Vampire Survivors (PC)&lt;/h4&gt;&#10;&lt;p&gt;Vampire Survivors looks like a Gamer Maker 6 demo project but it plays like an&#10;absolute dream. It is crack. I heard somewhere that the developer used to design&#10;gambling machines and I have no idea if that is true but it is also definitely&#10;true.&lt;/p&gt;&#10;&lt;h4 id="god-of-war-ragnarok-ps5"&gt;God of War: Ragnarok (PS5)&lt;/h4&gt;&#10;&lt;p&gt;I liked Dad of Boy quite a bit when it launched on the PS4, and Dad of Boy 2&#10;(or&amp;hellip;5? I&amp;rsquo;ve lost track) is more of the same, and that&amp;rsquo;s fine. The performances&#10;are excellent, the combat is entertaining and I like that we&amp;rsquo;re getting to see&#10;more of the Realms.&lt;/p&gt;&#10;&lt;h4 id="darktide-pc"&gt;Darktide (PC)&lt;/h4&gt;&#10;&lt;p&gt;I never really played Vermintide, but I joined up with my pals who did to get&#10;into Darktide right as the pre-order beta went live. It&amp;rsquo;s great fun. My guy is&#10;called Dorsworth and he&amp;rsquo;s a prick. His best friend is called Crump and he has&#10;a tense rivalry with Gideon and Rossell. I couldn&amp;rsquo;t tell you any more about the&#10;plot or whatever, Warhammer is not my area, but the little character stories&#10;that get generated through normal play are enough on their own.&lt;/p&gt;&#10;&lt;h4 id="the-excavation-of-hobs-barrow-pc"&gt;The Excavation of Hob&amp;rsquo;s Barrow (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2022/12/hobs-barrow.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Hob&amp;rsquo;s Barrow is a point-and-click adventure game with some really excellent&#10;voice acting and a compelling plot. It avoids a lot of the more tedious tropes&#10;of the genre and zips along at a lovely pace. It&amp;rsquo;s great. No spoilers please,&#10;I haven&amp;rsquo;t finished it yet.&lt;/p&gt;&#10;&lt;h4 id="signalis-xbox"&gt;Signalis (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;Signalis is a game that lots of &lt;a href="https://www.eurogamer.net/signalis-review-a-sumptuously-atmopsheric-survival-horror"&gt;cool people&lt;/a&gt; have &lt;a href="https://www.eurogamer.net/games-of-2022-signalis-was-the-best-reminder-of-what-matters-1"&gt;opinions about&lt;/a&gt;.&#10;I&amp;rsquo;ve only played the first few hours, so I don&amp;rsquo;t have many opinions yet, but&#10;it&amp;rsquo;s a game that almost demands discussion.&lt;/p&gt;&#10;&lt;h4 id="dorfromantik-pc"&gt;Dorfromantik (PC)&lt;/h4&gt;&#10;&lt;p&gt;I only played a little of this, but it&amp;rsquo;s a lovely tactile toylike thing.&lt;/p&gt;&#10;&lt;h4 id="golden-sun-gba"&gt;Golden Sun (GBA)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;d played the start of Golden Sun several times on various emulators over the&#10;years, but I finally got a proper GBA copy of it this year and I&amp;rsquo;ve been playing&#10;in earnest.&lt;/p&gt;&#10;&lt;p&gt;Golden Sun is an incredible technical accomplishment for the system it was made&#10;for, with a huge amount of dynamism in its systems outside combat that make it&#10;feel more like an adventure game than a by-the-numbers JRPG.&lt;/p&gt;&#10;&lt;p&gt;Something that really struck me was the use of colour - Golden Sun launched two&#10;years prior to the GBA SP, so its colour palette is chosen around the muddy&#10;screen of the original model. Thankfully the Analogue Pocket has display modes&#10;that let you simulate the various models, but playing this on an emulator is&#10;going to look garishly oversaturated.&lt;/p&gt;&#10;&lt;h4 id="chicory-switch"&gt;Chicory (Switch)&lt;/h4&gt;&#10;&lt;p&gt;It&amp;rsquo;s a bit unfortunate we only write these sorts of lists at the end of the&#10;year, isn&amp;rsquo;t it? Had I started it earlier than yesterday, I might have a &lt;em&gt;lot&lt;/em&gt;&#10;more to say about Chicory. It captures that same uncynical glee that Wandersong&#10;did so well. I love it so far, even though I can&amp;rsquo;t draw.&lt;/p&gt;&#10;&lt;h4 id="honourable-mentions"&gt;Honourable mentions&lt;/h4&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;I played quite a lot of nonsense anime game Scarlet Nexus this year. I am not&#10;recommending you play it. The combat is engaging, but the writing is truly&#10;awful. Character motivations make zero sense; someone might be doing their&#10;utmost to see you dead and then invite you out for tea and a chat about&#10;gardening. It&amp;rsquo;s bizarre - and yet, there&amp;rsquo;s something about the game I find&#10;utterly compelling. It&amp;rsquo;s like a horrible, disgusting junk food that I can&amp;rsquo;t stop&#10;eating. Scarlet Nexus is the cheesy gravy chips of video games.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;After some encouragement from good pal Ally Glennie I finally, &lt;em&gt;finally&lt;/em&gt; got&#10;around to finishing Breath of the Wild. I really went for it, too - I finished&#10;the DLC and cleared every shrine. BOTW might be the best game ever made, and&#10;that is in no way an original take but I think it works.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Deathloop is real good.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;Have a lovely 2023. Be kind to yourself. I really hope Tears of the Kingdom is&#10;good.&lt;/p&gt;</content:encoded></item><item><title>Packaging Android NDK APKs without Gradle</title><link>https://www.joelotter.com/posts/2022/10/android-ndk-without-gradle/</link><pubDate>Sun, 09 Oct 2022 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2022/10/android-ndk-without-gradle/</guid><description>&lt;p&gt;I struggled recently to work out how to build an APK out of a native Android&#10;app without using Gradle.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I struggled recently to work out how to build an APK out of a native Android&#10;app without using Gradle.&lt;/p&gt;&#10;&lt;p&gt;Android is pretty good these days at letting you build your app without needing&#10;to write any Java, thanks to NativeActivity and Native App Glue, but even in the&#10;official &lt;a href="https://github.com/android/ndk-samples/tree/main/native-activity"&gt;native activity example&lt;/a&gt; Gradle is still used as the build tool in order to package up an APK.&lt;/p&gt;&#10;&lt;p&gt;In a C++-based codebase, such as in my case a multiplatform game engine, we&#10;really don&amp;rsquo;t want to introduce additional build tools and configurations just&#10;for one platform. It would be great if we could instead put together our APK&#10;using just the command line and the tools provided in the NDK. Turns out we&#10;can!&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m going to assume you&amp;rsquo;re already set up with the NDK and so on. I have my&#10;Android SDK root location in the environment variable &lt;code&gt;ANDROID_SDK&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h2 id="building"&gt;Building&lt;/h2&gt;&#10;&lt;p&gt;For the purposes of this tutorial I&amp;rsquo;m going to use the&#10;&lt;a href="https://github.com/android/ndk-samples/tree/main/native-activity"&gt;native activity example&lt;/a&gt; from the ndk-samples repository. If you&amp;rsquo;ve already built your Android&#10;application into a shared native library then feel free to skip this section.&lt;/p&gt;&#10;&lt;p&gt;Within the ndk-samples repo, navigate to &lt;code&gt;native-activity/app/src/main/cpp&lt;/code&gt;.&#10;I&amp;rsquo;m building the shared library binary using CMake&amp;rsquo;s built-in Android support -&#10;if you&amp;rsquo;re using the old toolchain or similar feel free to modify the command to&#10;suit your build setup.&lt;/p&gt;&#10;&lt;p&gt;I create a &lt;code&gt;build&lt;/code&gt; directory for an out-of tree build, navigate into it, and run&#10;the following incantation:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cmake -DCMAKE_SYSTEM_NAME&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Android&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DCMAKE_ANDROID_NDK&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ANDROID_SDK&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/ndk/25.1.8937393&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="c1"&gt;# NDK location for CMake&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DANDROID_NDK&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ANDROID_SDK&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/ndk/25.1.8937393&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="c1"&gt;# NDK location needed by the project&amp;#39;s CMakeLists.txt&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ..&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And build it:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;make&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should now have a &lt;code&gt;libnative-activity.so&lt;/code&gt; in your build directory.&lt;/p&gt;&#10;&lt;h2 id="packaging"&gt;Packaging&lt;/h2&gt;&#10;&lt;p&gt;We need to set up the directory structure that will represent the inside of our&#10;APK. We just need to define where our shared library object will live. This is&#10;dependent on which ABI you&amp;rsquo;re building against - I&amp;rsquo;m using armeabi-v7a here.&lt;/p&gt;&#10;&lt;p&gt;Make a directory structure like this. The top-level name &lt;code&gt;apk&lt;/code&gt; here can be&#10;whatever you like, it doesn&amp;rsquo;t get bundled.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apk/&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; lib/&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; armeabi-v7a/&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can create it like so:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir -p apk/lib/armeabi-v7a&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For the sake of speed I&amp;rsquo;m going to strip the library and put it into our&#10;directory structure. If you want to keep your debug symbols in, you can just&#10;copy it. Note that the path to your toolchain binaries will vary depending on&#10;which platform you&amp;rsquo;re working on (I&amp;rsquo;m on macOS, hence &lt;code&gt;darwin-x86_64&lt;/code&gt;).&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ANDROID_SDK&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/ndk/25.1.8937393/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-strip&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; libnative-activity.so &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -o ./apk/lib/armeabi-v7a/libnative-activity.so&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now it&amp;rsquo;s time to create the actual APK:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ANDROID_SDK&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/build-tools/30.0.3/aapt&amp;#34;&lt;/span&gt; package &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -f &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="c1"&gt;# Overwrite the APK if it exists&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -M ../../AndroidManifest.xml &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="c1"&gt;# The manifest&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -I ~/Code/android-sdk/platforms/android-33/android.jar &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="c1"&gt;# The Android library&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -S ../../res &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="c1"&gt;# Resources (icons, values, etc.)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -F apk-unaligned.apk &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="c1"&gt;# Output file&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; apk &lt;span class="c1"&gt;# Our directory structure we created previously&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="align-and-sign"&gt;Align and sign&lt;/h2&gt;&#10;&lt;p&gt;Before the APK can be installed on a device, it needs to be zipaligned and&#10;signed.&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;zipalign&lt;/code&gt; is a tool which aligns files within an archive (like our APK)&#10;relative to the start of the file. This means your app can read stuff out of&#10;the APK like it would read from memory directly, rather than needing to copy&#10;stuff into RAM first.&lt;/p&gt;&#10;&lt;p&gt;The incantation is like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ANDROID_SDK&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/build-tools/30.0.3/zipalign&amp;#34;&lt;/span&gt; -f &lt;span class="m"&gt;4&lt;/span&gt; apk-unaligned.apk apk-unsigned.apk&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;re probably already familiar with signing - it validates that the APK is&#10;created by us and not a malicious third party. Assuming you&amp;rsquo;ve already set up a&#10;debug or release keychain, you can sign like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ANDROID_SDK&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/build-tools/30.0.3/apksigner&amp;#34;&lt;/span&gt; sign &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --ks ~/.android/debug.keystore &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --ks-key-alias&lt;span class="o"&gt;=&lt;/span&gt;androiddebugkey &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --out apk-signed.apk &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; apk-unsigned.apk&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Enter your password (it&amp;rsquo;s &lt;code&gt;android&lt;/code&gt; for the default debug keystore) and you&amp;rsquo;ll&#10;have a signed APK ready to run on a device!&lt;/p&gt;&#10;&lt;h2 id="deploying"&gt;Deploying&lt;/h2&gt;&#10;&lt;p&gt;You can install your new APK onto a real Android device or emulator using &lt;code&gt;adb&lt;/code&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ANDROID_SDK&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/platform-tools/bin/adb&amp;#34;&lt;/span&gt; install -r apk-signed.apk&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s the native example running on my relatively ancient HTC One M8. It&amp;rsquo;s on&#10;Android 5! Android&amp;rsquo;s backwards-compatibility is impressive.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2022/10/nativeexample.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Be aware that this APK will &lt;em&gt;only&lt;/em&gt; support devices with the ABI you built&#10;against. You can either build your library for each ABI (a so-called &amp;lsquo;fat APK&amp;rsquo;)&#10;or you can create one of the newer &lt;a href="https://developer.android.com/guide/app-bundle"&gt;Android App Bundles&lt;/a&gt;, which I&amp;rsquo;ll cover in a separate blog post once I work out how the hell they&#10;work.&lt;/p&gt;&#10;&lt;p&gt;So far I&amp;rsquo;ve been pretty impressed by Android&amp;rsquo;s tooling and rather less impressed&#10;by its documentation. Let&amp;rsquo;s see how things progress.&lt;/p&gt;</content:encoded></item><item><title>Reverse-engineering Daylio backups</title><link>https://www.joelotter.com/posts/2022/01/daylio/</link><pubDate>Sun, 30 Jan 2022 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2022/01/daylio/</guid><description>&lt;p&gt;I use an app called &lt;a href="https://daylio.net/"&gt;Daylio&lt;/a&gt; as a diary and a mood&#10;tracker. It&amp;rsquo;s really good! I recommend it. &lt;/p&gt;&#10;&lt;p&gt;I also use an app called Habit to track things I want to do every day, like&#10;&amp;ldquo;stay off social media&amp;rdquo; or &amp;ldquo;work on a project&amp;rdquo;.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I use an app called &lt;a href="https://daylio.net/"&gt;Daylio&lt;/a&gt; as a diary and a mood&#10;tracker. It&amp;rsquo;s really good! I recommend it. &lt;/p&gt;&#10;&lt;p&gt;I also use an app called Habit to track things I want to do every day, like&#10;&amp;ldquo;stay off social media&amp;rdquo; or &amp;ldquo;work on a project&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;Recently, Daylio added a new &amp;lsquo;Goals&amp;rsquo; feature, which pretty neatly replaces the&#10;functionality of Habit, while integrating into Daylio&amp;rsquo;s rather nice stat&#10;tracking stuff. Of course, there&amp;rsquo;s no &amp;ldquo;import Habit data&amp;rdquo; button, but both apps&#10;support exporting data in either human-readable or backup formats - how hard&#10;could it be to migrate the data over?&lt;/p&gt;&#10;&lt;p&gt;Not that hard, it turns out! I had a fun time with it, too. Here&amp;rsquo;s what I did.&lt;/p&gt;&#10;&lt;h2 id="the-daylio-file-format"&gt;The .daylio file format&lt;/h2&gt;&#10;&lt;p&gt;Daylio provides two options for exporting your data - either as a backup file&#10;(with a &lt;code&gt;.daylio&lt;/code&gt; extension) or as a PDF or CSV. It does warn you that the latter&#10;two options can not be re-ingested into the app; that is, we can&amp;rsquo;t edit a CSV&#10;and then import it again. The only real option is to somehow edit the backup.&lt;/p&gt;&#10;&lt;p&gt;First things first, let&amp;rsquo;s try and open the file in a text editor and hope&#10;against all reason that it&amp;rsquo;s JSON or something.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2022/1/daylio1.png" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Bum. That looks like binary. I &lt;em&gt;really&lt;/em&gt; do not want to get into&#10;reverse-engineering binary formats from a hexdump or similar; I&amp;rsquo;m just not smart&#10;enough. Let&amp;rsquo;s see what &lt;code&gt;file&lt;/code&gt; thinks about it.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2022/1/daylio2.png" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s a zip file with a silly extension! Fabulous. Let&amp;rsquo;s extract it and see what&#10;the craic is.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2022/1/daylio3.png" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;The archive contains &lt;em&gt;another&lt;/em&gt; &lt;code&gt;.daylio&lt;/code&gt; file, though we can see that thankfully&#10;this one isn&amp;rsquo;t a zip archive. I&amp;rsquo;m using a blank Daylio export for the purposes&#10;of not leaking my &amp;ldquo;dear diary&amp;rdquo; entries; if you&amp;rsquo;ve attached photos to any Daylio&#10;entries these appear in an &lt;code&gt;assets/&lt;/code&gt; directory inside the archive.&lt;/p&gt;&#10;&lt;p&gt;Let&amp;rsquo;s open up the new &lt;code&gt;.daylio&lt;/code&gt; file. I hope it&amp;rsquo;s JSON or something.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2022/1/daylio4.png" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Alas! Foiled again. However, this one is just text rather than Vim attempting to&#10;render binary. In fact, it looks an awful lot like Base64. Let&amp;rsquo;s see what&#10;happens if we run it through a decoder.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2022/1/daylio5.png" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;That looks like JSON. Excellent! We&amp;rsquo;re in business. I don&amp;rsquo;t know what it says&#10;about me that I can recognise Base64 just by looking at it, but it can&amp;rsquo;t be&#10;anything good.&lt;/p&gt;&#10;&lt;h2 id="dayligo"&gt;dayligo&lt;/h2&gt;&#10;&lt;p&gt;The next step is to write some software to parse the Daylio JSON and allow us&#10;to edit it. I ended up going with Golang for this - not for any special reason,&#10;I just like it.&lt;/p&gt;&#10;&lt;p&gt;The resulting library, including the program to import the Habit entries into&#10;Daylio, is on GitHub at&#10;&lt;a href="https://github.com/JoelOtter/dayligo"&gt;JoelOtter/dayligo&lt;/a&gt;, a name I am&#10;unjustifiably pleased with.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m not going to go through the code in great detail, because that would be&#10;very boring. Instead, I encourage you to have a look through if you&amp;rsquo;re&#10;interested. Here are a few things I thought were worth noting.&lt;/p&gt;&#10;&lt;h4 id="backup-json-format"&gt;Backup JSON format&lt;/h4&gt;&#10;&lt;p&gt;Daylio backups use three different root-level objects for describing goals:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;&amp;quot;goals&amp;quot;&lt;/code&gt; - the goals themselves. These can be named, or linked to a &amp;ldquo;tag&amp;rdquo;.&#10;Tags are &amp;ldquo;things you did on a day&amp;rdquo;, e.g. &amp;ldquo;projects&amp;rdquo; or &amp;ldquo;exercise&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;&amp;quot;goalEntries&amp;quot;&lt;/code&gt; - these represent a &amp;ldquo;completed&amp;rdquo; goal, with an associated&#10;goal ID and timestamp.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;&amp;quot;goalSuccessWeeks&amp;quot;&lt;/code&gt; - these represent weeks of the year in which a goal was&#10;fully completed. There&amp;rsquo;s a bit of weirdness to this as a goal aim can be every&#10;day, such as in habit, or a few other options like &amp;ldquo;three times a week&amp;rdquo;.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;The structure of the file as represented in JSON is parsed using the types in&#10;&lt;a href="https://github.com/JoelOtter/dayligo/blob/master/structure.go"&gt;&lt;code&gt;structure.go&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;The process of reverse-engineering these files was basically to export one, then&#10;adjust the clock on my phone to add a load more goal entries. Crucially, Daylio&#10;does &lt;em&gt;not&lt;/em&gt; allow you to backdate &amp;ldquo;goal success weeks&amp;rdquo;, so while it would have&#10;been trivial to naively import the habits as goals the &amp;ldquo;streak&amp;rdquo; would not have&#10;been correct. Given it&amp;rsquo;s the streak that motivates me, this wasn&amp;rsquo;t acceptable.&lt;/p&gt;&#10;&lt;h4 id="the-habit-script"&gt;The Habit script&lt;/h4&gt;&#10;&lt;p&gt;The script for importing the Habit files lives at &lt;a href="https://github.com/JoelOtter/dayligo/tree/master/cmd/import-habit"&gt;&lt;code&gt;cmd/import-habit&lt;/code&gt;&lt;/a&gt;. It doesn&amp;rsquo;t&#10;do anything particularly clever with the Habit file, which is a plain ol&amp;rsquo; CSV.&#10;I did have to cheat a little bit by editing the Habit file so that goal names&#10;matched; getting the script to resolve these in a clever way felt like a waste&#10;of time.&lt;/p&gt;&#10;&lt;h4 id="golang-specific-json-bits"&gt;Golang-specific JSON bits&lt;/h4&gt;&#10;&lt;p&gt;The dayligo import/export process stores an internal string-to-JSON-message map&#10;as well as a reference to a temporary directory. This is because I didn&amp;rsquo;t want&#10;to define types for every bit of the JSON file, but wanted to be able to marshal&#10;my updated backup back to JSON without losing any of that data. The &amp;ldquo;Golang way&amp;rdquo;&#10;to do that is to parse the JSON as &lt;code&gt;map[string]json.RawMessage&lt;/code&gt;. The full type&#10;definition of a Daylio backup is as follows.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Backup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DayEntries&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;DayEntry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;dayEntries&amp;#34;`&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Goals&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;Goal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;goals&amp;#34;`&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;GoalEntries&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;GoalEntry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;goalEntries&amp;#34;`&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;GoalSuccessWeeks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;GoalSuccessWeek&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;goalSuccessWeeks&amp;#34;`&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Tags&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;Tag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;tags&amp;#34;`&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`json:&amp;#34;version&amp;#34;`&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rawMap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RawMessage&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tempDirPath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The functions for importing and exporting these .daylio files (the full archive&#10;ones) live in &lt;a href="https://github.com/JoelOtter/dayligo/blob/master/file.go"&gt;&lt;code&gt;file.go&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h4 id="id-ordering"&gt;ID ordering&lt;/h4&gt;&#10;&lt;p&gt;There&amp;rsquo;s some assorted oddness with how the IDs get ordered in the Daylio&#10;entries; they appear to in some cases have IDs assigned by &lt;em&gt;newest first&lt;/em&gt;. I&#10;hope this is just an artifact of the export process and internally it is&#10;formatted somehow differently; re-assigning every ID on a new entry would be&#10;a crime against God. I sought to emulate this ordering where possible, but I&#10;suspect it doesn&amp;rsquo;t actually make a difference to the import.&lt;/p&gt;&#10;&lt;h4 id="iso-week"&gt;ISO week&lt;/h4&gt;&#10;&lt;p&gt;Goal success weeks have a &amp;ldquo;week&amp;rdquo; number assigned to them. I found this quite&#10;confusing at first, but I believe it&amp;rsquo;s actually &lt;a href="https://en.wikipedia.org/wiki/ISO_week_date"&gt;ISO week&lt;/a&gt;, a concept I&amp;rsquo;d not heart of before;&#10;thankfully Golang provides &lt;a href="https://pkg.go.dev/time#Time.ISOWeek"&gt;functionality&lt;/a&gt;&#10;to get this and the year the week &amp;ldquo;belongs&amp;rdquo; to. Go&amp;rsquo;s time handling stuff remains&#10;quite impressive.&lt;/p&gt;&#10;&lt;p&gt;I think that&amp;rsquo;s all the interesting bits. Interesting to me, at any rate. Have a&#10;nice day.&lt;/p&gt;</content:encoded></item><item><title>The games of 2021</title><link>https://www.joelotter.com/posts/2021/12/games-played-2021/</link><pubDate>Fri, 31 Dec 2021 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2021/12/games-played-2021/</guid><description>&lt;p&gt;I&amp;rsquo;m genuinely impressed that 2021 managed to be a generally worse year than&#10;2020, for a variety of fun reasons. It had some high highs but also some very&#10;low lows. It was a year in which I mostly quit social media (a positive&#10;change!), which means I was out of The Games Discourse for the last third. Do I&#10;miss it? Like hell.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I&amp;rsquo;m genuinely impressed that 2021 managed to be a generally worse year than&#10;2020, for a variety of fun reasons. It had some high highs but also some very&#10;low lows. It was a year in which I mostly quit social media (a positive&#10;change!), which means I was out of The Games Discourse for the last third. Do I&#10;miss it? Like hell.&lt;/p&gt;&#10;&lt;p&gt;Despite this, I &lt;em&gt;did&lt;/em&gt; play some videogames in 2021. 49 of them for the first&#10;time, which is only a few less than 2020. This was in a large part thanks to&#10;Game Pass, which turned my consumption of the medium from à la carte to a&#10;buffet, which I believe is the intention.&lt;/p&gt;&#10;&lt;p&gt;It was also thanks to being pals with some folks with excellent taste. I have by&#10;this point adopted a policy that anything&#10;&lt;a href="https://www.patreon.com/sammgreer"&gt;Sam Greer&lt;/a&gt; recommends is something that I&#10;really ought to play, and that hasn&amp;rsquo;t let me down yet.&lt;/p&gt;&#10;&lt;p&gt;As well as plugging away at&#10;&lt;a href="https://store.steampowered.com/app/863400/Feud/"&gt;Feud&lt;/a&gt;, and my ongoing work on&#10;&lt;a href="https://www.playscavengers.com/en"&gt;Scavengers&lt;/a&gt;, I also released a new&#10;game-like thing this year. As part of&#10;&lt;a href="https://twitter.com/raremammals"&gt;Rare Mammals&lt;/a&gt;, with excellent human Antosh&#10;Wojcik, I made&#10;&lt;a href="https://raremammals.itch.io/a-lake-in-america"&gt;A LAKE IN AMERICA&lt;/a&gt;, a weird&#10;poem-game with themes of hypochondria and health anxiety. Please check it out.&lt;/p&gt;&#10;&lt;p&gt;As ever, this list doesn&amp;rsquo;t include anything I bounced off immediately or played&#10;for less than an hour or so (unless it was short). It also doesn&amp;rsquo;t include&#10;anything I had already started playing prior to 2021, so while Hades and&#10;Assassin&amp;rsquo;s Creed Oranges consumed a lot of my time, they don&amp;rsquo;t count.&lt;/p&gt;&#10;&lt;p&gt;You can see my lists from previous years here:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2020/12/games-played-2020"&gt;2020&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2019/12/games-played-2019"&gt;2019&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2018/12/games-played-2018"&gt;2018&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.joelotter.com/posts/2017/12/games-played-2017"&gt;2017&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;The games of 2021 were as follows.&lt;/p&gt;&#10;&lt;h4 id="ghost-of-tsushima-ps4"&gt;Ghost of Tsushima (PS4)&lt;/h4&gt;&#10;&lt;p&gt;Ghost of Tsushima has a lot of the ingredients of something I&amp;rsquo;d quite like, but&#10;unfortunately it rather falls apart in the execution. Its rendering of Tsushima&#10;is a beautiful place, but the very claustrophobic third-person camera means you&#10;never really feel like you&amp;rsquo;re able to fully appreciate it. Couple that with&#10;some really quite dreary writing, and I&amp;rsquo;m sorry to say I gave up on this one&#10;after just a few hours.&lt;/p&gt;&#10;&lt;p&gt;That said, my brother played about 45 hours of this and loved it, so if you&#10;think it&amp;rsquo;s your thing by all means go for it.&lt;/p&gt;&#10;&lt;h4 id="alba-a-wildlife-adventure-ios"&gt;Alba: A Wildlife Adventure (iOS)&lt;/h4&gt;&#10;&lt;p&gt;Alba is a pretty little adventure game that I think largely achieves what it&#10;sets out to do. The setting is a joy to explore and the photography mechanic is&#10;good fun. It also has a pretty inspired user interface if you play it without a&#10;controller where you rotate the phone for different situations. I liked it quite&#10;a bit.&lt;/p&gt;&#10;&lt;h4 id="nuts-ios"&gt;NUTS (iOS)&lt;/h4&gt;&#10;&lt;p&gt;NUTS is a strange game. The art direction is really striking and it has the&#10;&lt;em&gt;tone&lt;/em&gt; of a creeping horror game, yet as far as I could tell from my few hours&#10;with it that isn&amp;rsquo;t what this game is or is even trying to be. Perhaps there&amp;rsquo;s&#10;some late-game twist but I found the mechanics too clunky to continue beyond&#10;the first couple of hours.&lt;/p&gt;&#10;&lt;p&gt;This was the first game I played with a&#10;&lt;a href="https://playbackbone.com/en-GB/"&gt;Backbone&lt;/a&gt; controller, which I&amp;rsquo;d really&#10;recommend if you find yourself playing on iOS a lot.&lt;/p&gt;&#10;&lt;h4 id="13-sentinels-aegis-rim-ps4"&gt;13 Sentinels: Aegis Rim (PS4)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2021/12/13sentinels.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s really hard to write about 13 Sentinels while remaining succinct; such is&#10;the scope and ambition of the story it&amp;rsquo;s telling and also how much I adored it.&lt;/p&gt;&#10;&lt;p&gt;The thing I will say is that its storytelling is of a sort that I don&amp;rsquo;t think&#10;could be easily achieved outside of the medium of videogames, one where it&#10;actually goes so far as to justify the medium. This isn&amp;rsquo;t due to the plot&#10;itself, which is good, but because of how it unfolds; the player is encouraged&#10;to unravel the mystery of what the hell is going on in whichever order they see&#10;fit. This means that the answers you gradually uncover and the realisations you&#10;come to feel &lt;em&gt;earned&lt;/em&gt;, rather than just dropped as exposition.&lt;/p&gt;&#10;&lt;p&gt;I can recommend this one without hesitation, even if you think the very anime&#10;vibes are not for you. It&amp;rsquo;s out on Switch in 2022, I believe. Tied for my game&#10;of the year, despite being technically a 2020 release.&lt;/p&gt;&#10;&lt;h4 id="disc-room-pc"&gt;Disc Room (PC)&lt;/h4&gt;&#10;&lt;p&gt;Disc Room is a videogame-ass-videogame and I love it for that. I had high&#10;expectations from something Jan Willem Nijman worked on and this didn&amp;rsquo;t&#10;disappoint; it feels incredibly tuned and precise in a way that pitches the&#10;difficulty and responsiveness &lt;em&gt;just&lt;/em&gt; right. A treat.&lt;/p&gt;&#10;&lt;h4 id="loop-hero-pc"&gt;Loop Hero (PC)&lt;/h4&gt;&#10;&lt;p&gt;Loop Hero takes inspiration from roguelites and idle games, and is somehow both&#10;and simultaneously neither. The &amp;ldquo;one more loop&amp;rdquo; mechanic is really compelling,&#10;and it&amp;rsquo;s also got a really interesting thing going with its tone and setting.&#10;Unfortunately this is also a game that I am &lt;em&gt;very&lt;/em&gt; bad at, and will probably&#10;never manage to see all the interesting stuff that comes later in it.&lt;/p&gt;&#10;&lt;h4 id="lair-of-the-clockwork-god-switch"&gt;Lair of the Clockwork God (Switch)&lt;/h4&gt;&#10;&lt;p&gt;I need to get back to this one. It combines classic point-and-click adventure&#10;style mechanics with some puzzle-platformer stuff, and does it in a way that is&#10;hilariously self-aware. I was prepared to find the writing quite irritating&#10;(after Journey to the Savage Planet last year I have a deep suspicion of &amp;lsquo;funny&amp;rsquo;&#10;games) but it actually made me chuckle quite a bit.&lt;/p&gt;&#10;&lt;h4 id="cloud-gardens-pc"&gt;Cloud Gardens (PC)&lt;/h4&gt;&#10;&lt;p&gt;A lovely peaceful terrarium of a thing. It&amp;rsquo;s a really nice way to unwind after a&#10;stressy day. Looks stunning, too.&lt;/p&gt;&#10;&lt;h4 id="paradise-killer-pc"&gt;Paradise Killer (PC)&lt;/h4&gt;&#10;&lt;p&gt;I didn&amp;rsquo;t get too far into this bonkers detective-ish game but in tone and&#10;presentation it&amp;rsquo;s absolutely incredible. Characters have names like &amp;ldquo;Doctor Doom&#10;Jazz&amp;rdquo; and the setting feels like nothing so much as a haunted, abandoned&#10;Minecraft server. It&amp;rsquo;s really good.&lt;/p&gt;&#10;&lt;h4 id="valheim-pc"&gt;Valheim (PC)&lt;/h4&gt;&#10;&lt;p&gt;I spent a very pleasant evening playing this survival-crafting game with good&#10;pal Ally. I focused on getting our chimney to look exactly how I wanted it, he&#10;went off exploring. Truly, something for everyone.&lt;/p&gt;&#10;&lt;h4 id="assemble-with-care-ios"&gt;Assemble With Care (iOS)&lt;/h4&gt;&#10;&lt;p&gt;I found the concept of &amp;ldquo;repair broken objects&amp;rdquo; quite compelling. Unfortunately,&#10;the &amp;ldquo;puzzles&amp;rdquo; never require much thought, and the whole thing is overlaid by an&#10;utterly cloying plot and a script that I found borderline insufferable.&lt;/p&gt;&#10;&lt;h4 id="good-sudoku-ios"&gt;Good Sudoku (iOS)&lt;/h4&gt;&#10;&lt;p&gt;The pitch of Good Sudoku is &amp;ldquo;does the easy, boring bits so you can focus on the&#10;hard, more interesting bits&amp;rdquo;. I think it accomplishes this pretty nicely - you&#10;pass right over the hunting-for-numbers bit in the puzzles and go straight to&#10;learning the more advanced strategies with names like &amp;ldquo;X-wing&amp;rdquo; and &amp;ldquo;locked&#10;candidate&amp;rdquo;. The presentation is really slick, too.&lt;/p&gt;&#10;&lt;h4 id="concrete-genie-ps4"&gt;Concrete Genie (PS4)&lt;/h4&gt;&#10;&lt;p&gt;I gave this one a go when it came up on PS Plus, and found it to be a fairly&#10;slick and enjoyable action-ish game. The story hits the right balance of sweet&#10;without being saccharine, and while the painting mechanic is perhaps a little&#10;underutilised it&amp;rsquo;s certainly at least unique.&lt;/p&gt;&#10;&lt;h4 id="nier-replicant-ps4"&gt;NieR Replicant (PS4)&lt;/h4&gt;&#10;&lt;p&gt;NieR Automata was a surprise hit for me; after a rocky start I ended up adoring&#10;it. Replicant hits a lot of those same notes, but I didn&amp;rsquo;t find it compelling&#10;enough to see it through in one go. I do intend to go back to it to find out&#10;what the hell is going on.&lt;/p&gt;&#10;&lt;h4 id="returnal-ps5"&gt;Returnal (PS5)&lt;/h4&gt;&#10;&lt;p&gt;There was a lot of capital-D Discourse around this game at launch, about its&#10;price, difficulty and lack of sensible save options. I&amp;rsquo;m not going to talk about&#10;those things because other, smarter people have.&lt;/p&gt;&#10;&lt;p&gt;What I will say is that Returnal evokes Metroid Prime in a way that no other&#10;game ever really has for me, and in a way I can&amp;rsquo;t really explain. Anyway it&#10;slaps and I want to play more of it.&lt;/p&gt;&#10;&lt;h4 id="the-legend-of-zelda-the-minish-cap-gba"&gt;The Legend of Zelda: The Minish Cap (GBA)&lt;/h4&gt;&#10;&lt;p&gt;OK, I technically played this one in [&lt;em&gt;frantic Googling&lt;/em&gt;] 2004, but I think it&amp;rsquo;s&#10;been long enough that this can count.&lt;/p&gt;&#10;&lt;p&gt;I revisited this, along with a bunch of other GBA games, thanks to a&#10;&lt;a href="https://www.polygon.com/22640171/mister-project-classic-gaming-retro-fpga-board-chip-io-explainer-usb-hub"&gt;MiSTer&lt;/a&gt;&#10;unit I set up earlier in the year. A lot of these games hold up well but I think&#10;the Minish Cap in particular is a Zelda game that doesn&amp;rsquo;t get the recognition&#10;it deserves. It&amp;rsquo;s also some of the most beautiful pixel art and animation ever&#10;done on that hardware.&lt;/p&gt;&#10;&lt;h4 id="moss-pc"&gt;Moss (PC)&lt;/h4&gt;&#10;&lt;p&gt;A platform/action game where you control a wee mouse was always going to be&#10;good, but I think this one justifies the use of VR - by placing the player in&#10;the actual world as a sort of spirit, you feel embodied in a way some&#10;third-person VR games can lack. You can see your own reflection in the water and&#10;sometimes the wee mouse waves up at you. Lovely.&lt;/p&gt;&#10;&lt;h4 id="an-airport-for-aliens-currently-run-by-dogs-pc"&gt;An Airport For Aliens Currently Run By Dogs (PC)&lt;/h4&gt;&#10;&lt;p&gt;It&amp;rsquo;s a Nelson game. It&amp;rsquo;s very funny. You can pet &lt;em&gt;all&lt;/em&gt; the dogs and there&amp;rsquo;s an&#10;Orb. I love Orb.&lt;/p&gt;&#10;&lt;h4 id="yakuza-like-a-dragon-xbox"&gt;Yakuza: Like a Dragon (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;m not super well-versed in the Yakuza series - I loved 0 but that&amp;rsquo;s the extent&#10;of my experience - so by introducing a brand new protagonist Like A Dragon&#10;does a great job of bringing relative newcomers into the series. It&amp;rsquo;s got the&#10;usual blend of joyous stupidity and surprisingly good character writing. Not&#10;super keen on the turn-based combat, though.&lt;/p&gt;&#10;&lt;h4 id="going-under-xbox"&gt;Going Under (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;It&amp;rsquo;s a fairly standard dungeon crawling action game, but with great music and&#10;some beautifully snarky writing. Check it out if you like that sort of thing.&lt;/p&gt;&#10;&lt;h4 id="deaths-door-xbox"&gt;Death&amp;rsquo;s Door (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;This game is absolutely the definition of My Type On Paper. Zelda-ish top down&#10;camera and exploration? Wonderful. Soulsy, deliberate, precise combat? Fabulous.&#10;You&amp;rsquo;re a wee bird? Amazing. Unfortunately all these parts didn&amp;rsquo;t come together&#10;to make something that clicked for me. I understand why this was so lauded at&#10;launch, but for whatever reason I bounced off it fairly quickly.&lt;/p&gt;&#10;&lt;h4 id="the-ascent-xbox"&gt;The Ascent (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;From pre-release materials and so on I was somehow put under the impression this&#10;game was a turn-based tactics thing. If it had been I probably would have kept&#10;playing; the setting of the arcology is something I really would like to explore&#10;more. Sadly it&amp;rsquo;s a bad twin-stick shooter, so I couldn&amp;rsquo;t be bothered.&lt;/p&gt;&#10;&lt;h4 id="ikenfell-xbox"&gt;Ikenfell (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2021/12/ikenfell.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I &lt;em&gt;love&lt;/em&gt; Ikenfell. It&amp;rsquo;s reminiscent of the best GBA RPGs, full of heart and&#10;humour, and with a really unique and satisfying combat system that&amp;rsquo;s like a&#10;cross between Paper Mario and a tactics game. The only slight irritation is the&#10;occasional musical number during boss fights - and by musical I mean &amp;ldquo;sounds&#10;like it&amp;rsquo;s from a musical&amp;rdquo;. I hate musicals and the boss fights can be quite&#10;long. Alas.&lt;/p&gt;&#10;&lt;h4 id="deep-rock-galactic-xbox"&gt;Deep Rock Galactic (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;I think I tried this one earlier in the year and bounced off it, but in&#10;hindsight that was probably because this game is absolutely designed from the&#10;ground up (heh) as a co-op experience. In that it excels - the core loop is&#10;satisfying, and there are enough silly diversions between missions to keep the&#10;voice chat lively. Would&amp;rsquo;ve been a great lockdown game if I&amp;rsquo;d discovered it in&#10;time. Well, I guess we don&amp;rsquo;t know what 2022 holds here on Plague island.&lt;/p&gt;&#10;&lt;h4 id="halo-5-guardians-xbox"&gt;Halo 5: Guardians (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;I never got around to playing this one because until fairly recently I didn&amp;rsquo;t&#10;have anything I could play it on. It remains a bit of a strange artefact as the&#10;only mainline Halo game you can&amp;rsquo;t play on PC.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;d heard the campaign was dreadful, so I was pleasantly surprised to find that&#10;it&amp;rsquo;s&amp;hellip;OK? The plot is nonsense, but it&amp;rsquo;s Halo so of course it is. The shooting&#10;is OK. It doesn&amp;rsquo;t really feel like Halo, but neither did 4 and I think I like&#10;this one better.&lt;/p&gt;&#10;&lt;p&gt;It didn&amp;rsquo;t give me any confidence that Halo Infinite wouldn&amp;rsquo;t be shit, mind.&#10;Thankfully, as we&amp;rsquo;ll see in a bit, I needn&amp;rsquo;t have worried.&lt;/p&gt;&#10;&lt;h4 id="recompile-xbox"&gt;Recompile (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;I didn&amp;rsquo;t get too far with this Metroidvania-ish thing, but thanks to some&#10;stellar art direction I can at least say that the vibes are utterly immaculate.&#10;I intend to revisit this one.&lt;/p&gt;&#10;&lt;h4 id="ratchet-and-clank-rift-apart-ps5"&gt;Ratchet and Clank: Rift Apart (PS5)&lt;/h4&gt;&#10;&lt;p&gt;A lot of reviewers described the new Ratchet as a &amp;ldquo;playable Pixar film&amp;rdquo;. This is&#10;incorrect. Ratchet does&#10;&lt;a href="https://filmschoolrejects.com/the-dreamworks-face-must-be-stopped-2fcd4e80d6a6/"&gt;that face&lt;/a&gt;,&#10;so it&amp;rsquo;s a playable Dreamworks film.&lt;/p&gt;&#10;&lt;p&gt;Anyway, the game is pretty good. I don&amp;rsquo;t have a lot of nostalgia for this series&#10;as I never had a PS2 or PS3, but it holds up well in its own right and is, as&#10;many have commented, a fantastic showcase for the PS5 hardware. One of, like,&#10;three.&lt;/p&gt;&#10;&lt;h4 id="splitgate-pc"&gt;Splitgate (PC)&lt;/h4&gt;&#10;&lt;p&gt;&amp;ldquo;Halo 3 but with portals&amp;rdquo; ought to be a pretty easy sell, and for many people&#10;Splitgate is exactly that. Unfortunately all it really did was remind me of two&#10;games I&amp;rsquo;d really rather be playing.&lt;/p&gt;&#10;&lt;h4 id="deathloop-pc"&gt;DEATHLOOP (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2021/12/deathloop.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I played this one on PC, which meant I spent the first couple of hours with it&#10;fiddling with graphics settings instead of enjoying what is an extremely good&#10;game.&lt;/p&gt;&#10;&lt;p&gt;Dishonored was a game that punished you for playing it the most fun way,&#10;then Death of the Outsider removed the negative consequences for doing so.&#10;DEATHLOOP goes further, actively &lt;em&gt;forcing&lt;/em&gt; you to play it as an action game,&#10;leaving the save-scumming and sneaking behind. The result is some really&#10;dynamic, exciting action sequences that aren&amp;rsquo;t scripted but emergent from the&#10;game&amp;rsquo;s internal logic - exactly what the immersive sim has always promised but&#10;been prevented from delivering largely by its players.&lt;/p&gt;&#10;&lt;h4 id="assassins-creed-odyssey-pc"&gt;Assassin&amp;rsquo;s Creed Odyssey (PC)&lt;/h4&gt;&#10;&lt;p&gt;2021 was the year I finished Assassin&amp;rsquo;s Creed Origins, a game I liked a lot more&#10;than I thought I would. Once I&amp;rsquo;d wrapped up its story (which ended in a faintly&#10;unsatisfying way, I have to say) I immediately installed Odyssey.&lt;/p&gt;&#10;&lt;p&gt;This may have been a mistake. I spent a few hours in Odyssey and enjoyed them -&#10;I think Kassandra is better as a lead even than Bayek was - but in hindsight I&#10;just wasn&amp;rsquo;t ready for another open world game of this magnitude. I&amp;rsquo;ll probably&#10;revisit this one after a bit of a break.&lt;/p&gt;&#10;&lt;h4 id="fuser-pc"&gt;Fuser (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2021/12/fuser.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve no idea what being a DJ feels like but if it&amp;rsquo;s anything like playing Fuser&#10;I think it&amp;rsquo;d be great fun.&lt;/p&gt;&#10;&lt;p&gt;Harmonix have always made the sharpest, most polished music games, and Fuser&#10;fulfils that legacy wonderfully. There&amp;rsquo;s a story mode to play through&#10;but it&amp;rsquo;s enjoyable just messing around with the thing for hours on end. I missed&#10;this one at launch and I&amp;rsquo;m very glad I came across it this year.&lt;/p&gt;&#10;&lt;h4 id="metroid-dread-switch"&gt;Metroid Dread (Switch)&lt;/h4&gt;&#10;&lt;p&gt;If you&amp;rsquo;d told me a year ago we were getting a new Metroid game in 2021, &lt;em&gt;and&lt;/em&gt;&#10;it&amp;rsquo;d be Dread, the game I remember hearing rumours for as a DS game, I&amp;rsquo;d have&#10;laughed at you. And yet, here we are - Metroid 5 exists, it&amp;rsquo;s out, and it&amp;rsquo;s&#10;really good.&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s also &lt;em&gt;hard&lt;/em&gt;; I&amp;rsquo;ve got past the first big boss fight but not much further.&#10;Having a blast, though, and very keen to see how this series&amp;rsquo; story wraps up.&lt;/p&gt;&#10;&lt;h4 id="turnip-boy-commits-tax-evasion-pc"&gt;Turnip Boy Commits Tax Evasion (PC)&lt;/h4&gt;&#10;&lt;p&gt;A ludicrously silly Zelda-ish thing. It made me giggle several times. I don&amp;rsquo;t&#10;have much else to say about this one other than &amp;ldquo;I liked it&amp;rdquo;.&lt;/p&gt;&#10;&lt;h4 id="genshin-impact-iosps5"&gt;Genshin Impact (iOS/PS5)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;d always sort of observed Genshin from afar and had it summarised in my head&#10;as &amp;ldquo;bad Breath of the Wild rip-off with gacha mechanics&amp;rdquo;. What Genshin Impact&#10;&lt;em&gt;actually&lt;/em&gt; is is a surprisingly OK BOTW rip-off with some really quite&#10;competent writing and impressive mobile performance. I ended up quite liking it,&#10;though as a Destiny player I don&amp;rsquo;t really have time for another Forever Game.&lt;/p&gt;&#10;&lt;h4 id="sable-pcxbox"&gt;Sable (PC/Xbox)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2021/12/sable.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;The first time I tried to play Sable I bounced off it fairly quickly - its&#10;relative aimlessness in play, as well as poor console performance, was something&#10;I just wasn&amp;rsquo;t in the right headspace for.&lt;/p&gt;&#10;&lt;p&gt;Thankfully, after a bit of a break, and some patches, I was able to appreciate&#10;Sable for what it actually is - a staggeringly good-looking exploration game&#10;with some really lovely, warm writing. It&amp;rsquo;s a great big hug and I love it.&lt;/p&gt;&#10;&lt;h4 id="forza-horizon-5-pcxbox"&gt;Forza Horizon 5 (PC/Xbox)&lt;/h4&gt;&#10;&lt;p&gt;I actually took a day off work to play this at launch, such is my love of this&#10;series, which I recently heard someone describe as &amp;ldquo;Pokémon but with cars&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m glad I did - Horizon 5 delivers More Forza Horizon, exactly what I wanted,&#10;and does it extremely well. I do miss the more varied seasonal weather of FH4,&#10;but Mexico is an inspired setting. Also, the cars can do big jumps and I like it&#10;when they do that.&lt;/p&gt;&#10;&lt;h4 id="hunt-showdown-pc"&gt;Hunt: Showdown (PC)&lt;/h4&gt;&#10;&lt;p&gt;I played PUBG back in 2017, and experienced it essentially as a tourist - I&#10;played with someone much more experienced, joined their team, and just did&#10;exactly what they told me. It was great fun.&lt;/p&gt;&#10;&lt;p&gt;I played Hunt in a very similar way, with excellent humans Sam and Gary who&#10;know this game back-to-front. The setting is pretty unique in games, creating&#10;a tension that is entirely appropriate for its mechanics, and by playing with&#10;those two I was able to experience all the best bits without worrying too much&#10;about the fact I&amp;rsquo;m rubbish at it. Videogame guided tours ought to be a thing.&lt;/p&gt;&#10;&lt;h4 id="gravity-rush-ps-vita"&gt;Gravity Rush (PS Vita)&lt;/h4&gt;&#10;&lt;p&gt;I dug the Vita out of its drawer after watching Sam play Gravity Rush 2 in order&#10;to have a go at the first one. It&amp;rsquo;s great - the gravity mechanic gives you an&#10;enormous amount of freedom to explore its gorgeously realised world. I also&#10;really like how it uses the Vita hardware.&lt;/p&gt;&#10;&lt;h4 id="persona-4-golden-ps-vita"&gt;Persona 4 Golden (PS Vita)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2021/12/p4g.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I finished Persona 5 at the start of the year, and by the winter I was ready for&#10;- make that &lt;em&gt;craving&lt;/em&gt; - another game in the series. Persona 6 isn&amp;rsquo;t anywhere to&#10;be heard of just yet, so I went back to 4, playing on the Vita that seems like&#10;its natural home.&lt;/p&gt;&#10;&lt;p&gt;Knowing this game was originally a PS2 title meant I had quite low expectations&#10;- Persona 5 was so &lt;em&gt;stylish&lt;/em&gt;, would its predecessor be able to measure up in&#10;any way? I needn&amp;rsquo;t have worried - while not quite as gorgeous to look at as its&#10;big brother, Persona 4 has a huge amount of polish, style and heart that I now&#10;understand to be a property of this series as a whole, not just the latest&#10;entry.&lt;/p&gt;&#10;&lt;h4 id="exo-one-xbox"&gt;Exo One (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;Exo One is Tiny Wings that thinks it&amp;rsquo;s deep, but it actually manages to be a&#10;great experience all the same. It manages to create a feeling of speed and&#10;momentum in a way that gave me brain-shivers, and the just-hinted-at story is&#10;evocative enough to provide a nice breather between surprisingly intense levels.&lt;/p&gt;&#10;&lt;p&gt;I only wish that that feeling of speed and momentum wasn&amp;rsquo;t so difficult to&#10;achieve, and wasn&amp;rsquo;t so easy to have interrupted by slight jank that never really&#10;felt like my fault.&lt;/p&gt;&#10;&lt;p&gt;Speaking of jank&amp;hellip;&lt;/p&gt;&#10;&lt;h4 id="jett-the-far-shore-ps5"&gt;JETT: The Far Shore (PS5)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2021/12/jett.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;JETT is one of the most fascinating games I played this year. It is unlike&#10;anything else I&amp;rsquo;ve ever played, and uses both its narrative and mechanics to&#10;ask questions about exploration, colonisation and humanity&amp;rsquo;s place in a hostile&#10;universe.&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s also a game that could &lt;em&gt;really&lt;/em&gt; have done with another few months'&#10;development. It runs horribly on the latest console hardware, the controls are&#10;a little bit &lt;em&gt;broken&lt;/em&gt;, dialogue triggers at incorrect times, and it generally&#10;gives the impression of being a little bit unfinished. I think it speaks to&#10;JETT&amp;rsquo;s ambition and accomplishment that all of these things didn&amp;rsquo;t stop me&#10;really liking it.&lt;/p&gt;&#10;&lt;h4 id="solar-ash-ps5"&gt;Solar Ash (PS5)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2021/12/solarash.png" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Hyper Light Drifter is one of my all-time favourites. If you&amp;rsquo;ve not played it, I&#10;really recommend this Cool Ghosts (RIP)&#10;&lt;a href="https://www.youtube.com/watch?v=oWE-3aYTbhM"&gt;video&lt;/a&gt; so you can see what it&amp;rsquo;s&#10;all about. Needless to say, my expectations of Solar Ash were pretty damn high.&lt;/p&gt;&#10;&lt;p&gt;Did it meet them? Er, no. Solar Ash is in many ways a very by-the-numbers action&#10;game, one where HLD&amp;rsquo;s evocative silence is replaced by near-constant chatter.&#10;The art style is beautiful and it&amp;rsquo;s a very polished thing, but it gives the&#10;impression of something where the rough edges have been removed to the point&#10;where it&amp;rsquo;s just no longer that interesting. It stands in stark contrast to JETT,&#10;which I played just previously.&lt;/p&gt;&#10;&lt;p&gt;With all that being said, I ended up having a pretty great time with Solar Ash,&#10;even going so far as to 100% it, something I do with a vanishingly small number&#10;of games. This puts me in two minds about the thing, and I&amp;rsquo;m still trying to&#10;work out if I actually &lt;em&gt;liked&lt;/em&gt; it. I guess I did.&lt;/p&gt;&#10;&lt;h4 id="unpacking-pcxbox"&gt;Unpacking (PC/Xbox)&lt;/h4&gt;&#10;&lt;p&gt;Unpacking is mechanically simple, but this only serves to help it provide the&#10;experience it&amp;rsquo;s trying to. When you&amp;rsquo;re placing objects around the various rooms,&#10;you find yourself not &amp;ldquo;gaming&amp;rdquo; it, but genuinely thinking, &amp;ldquo;hmm, where would &lt;em&gt;I&lt;/em&gt;&#10;put this?&amp;rdquo;. It also has a really touching narrative that&amp;rsquo;s presented almost&#10;entirely through its mechanics, which is a great achievement.&lt;/p&gt;&#10;&lt;h4 id="infra-pc"&gt;INFRA (PC)&lt;/h4&gt;&#10;&lt;p&gt;Games made in the Source engine have something a bit special about them; they&#10;provide a sense of &lt;em&gt;place&lt;/em&gt; that their more advanced counterparts usually don&amp;rsquo;t.&#10;I have no idea why this is.&lt;/p&gt;&#10;&lt;p&gt;INFRA is no exception. It is a &lt;em&gt;very&lt;/em&gt; Source game. Lots of concrete. Good.&lt;/p&gt;&#10;&lt;h4 id="halo-infinite-xbox"&gt;Halo Infinite (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2021/12/halo.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;When I got put into isolation due to a positive COVID test, the first thought I&#10;had after I&amp;rsquo;d finished being sad about missing the Christmas build-up was &amp;ldquo;well,&#10;at least I&amp;rsquo;ll have a lot of time to play the new Halo&amp;rdquo;. I did.&lt;/p&gt;&#10;&lt;p&gt;I love Halo, and Halo Infinite is Good Halo in a way that the series hasn&amp;rsquo;t been&#10;since Bungie left the helm. The sandbox is exciting again, and the grapple hook&#10;changes the game&amp;rsquo;s mobility for the better while still somehow keeping the&#10;&amp;ldquo;Halo feel&amp;rdquo;, something at which Halo 5 didn&amp;rsquo;t succeed nearly as well. I also&#10;appreciate the story for &lt;em&gt;not&lt;/em&gt; requiring players to have read the novels -&#10;though as someone who actually has read some of the novels I did appreciate the&#10;occasional nod.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve yet to play much of the multiplayer but I suspect I&amp;rsquo;ll put a lot of time&#10;into it in 2022.&lt;/p&gt;&#10;&lt;h4 id="the-gunk-xbox"&gt;The Gunk (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;The Gunk is quite a departure from Image &amp;amp; Form&amp;rsquo;s SteamWorld setting, but it&#10;retains a lot of the good humour that&amp;rsquo;s in that studio&amp;rsquo;s DNA. It&amp;rsquo;s really&#10;beautiful to look at - there&amp;rsquo;s something of a claymation vibe to its textures -&#10;and tells a simple but compelling story about the conflict between industry and&#10;the environment.&lt;/p&gt;&#10;&lt;p&gt;The vacuuming mechanic gets a little bit stale by the end, but it doesn&amp;rsquo;t&#10;outstay its welcome. I think it&amp;rsquo;s well worth checking out if you have Game Pass.&lt;/p&gt;&#10;&lt;h4 id="the-forgotten-city-xbox"&gt;The Forgotten City (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2021/12/forgottencity.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s a toss-up between this and 13 Sentinels for my favourite game I played this&#10;year, and for very similar reasons. The Forgotten City is a game that lets the&#10;player unravel the mystery at their own pace, in the order they choose, and that&#10;makes the discoveries all the more rewarding.&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s also a wonderful setting - beautifully realised, and clear that real&#10;research went into making it as authentic as possible within the confines of its&#10;premise.&lt;/p&gt;&#10;&lt;p&gt;There&amp;rsquo;s very little I can say about this game without spoiling some aspect of&#10;it; I went in knowing almost nothing about it, and I&amp;rsquo;d recommend you do the&#10;same.&lt;/p&gt;&#10;&lt;h4 id="scarlet-nexus-xbox"&gt;Scarlet Nexus (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;I love me some anime bullshit, and Scarlet Nexus has some anime &lt;em&gt;bullshit&lt;/em&gt;. I&amp;rsquo;m&#10;about five hours in and the plot is already becoming incomprehensible even by&#10;the standards of the genre. It&amp;rsquo;s great fun.&lt;/p&gt;&#10;&lt;p&gt;It also features a combat system that&amp;rsquo;s a little bit Bayonetta and a little bit&#10;Genshin Impact - weird, I know, but it seems to work.&lt;/p&gt;&#10;&lt;h4 id="dandara-ios"&gt;Dandara (iOS)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2021/12/dandara.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Dandara came right in at the end of the year as something I&amp;rsquo;d heard of last year&#10;but never got around to trying. I played it on Apple Arcade, and, shockingly for&#10;a Metroidvania-ish thing, I think it&amp;rsquo;s perfectly at home on a touch screen. The&#10;main mechanic involves leaping between ceilings, walls and floors, and these&#10;jumps are plotted by swiping in the rough direction of travel, with some very&#10;aggressive target-snapping.&lt;/p&gt;&#10;&lt;p&gt;The result is something that feels really responsive in the hands. I&amp;rsquo;m only an&#10;hour or so in but it&amp;rsquo;s managed to hold my attention in a way few mobile games&#10;have this year.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;There&amp;rsquo;s your lot. Here&amp;rsquo;s hoping 2022 is a bit less shit. Get your jab, be nice&#10;to your ma and have a lovely day.&lt;/p&gt;</content:encoded></item><item><title>The games I played in 2020</title><link>https://www.joelotter.com/posts/2020/12/games-played-2020/</link><pubDate>Thu, 31 Dec 2020 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2020/12/games-played-2020/</guid><description>&lt;p&gt;2020 has been hot garbage. It has had very few redeeming factors and I am not&#10;appreciative of it at all. &lt;/p&gt;&#10;&lt;p&gt;Some great games, though.&lt;/p&gt;</description><content:encoded>&lt;p&gt;2020 has been hot garbage. It has had very few redeeming factors and I am not&#10;appreciative of it at all. &lt;/p&gt;&#10;&lt;p&gt;Some great games, though.&lt;/p&gt;&#10;&lt;p&gt;I like to post the list of games I played each year (&lt;a href="https://www.joelotter.com/posts/2017/12/games-played-2017"&gt;2017&lt;/a&gt;, &lt;a href="https://www.joelotter.com/posts/2018/12/games-played-2018"&gt;2018&lt;/a&gt;, &lt;a href="https://www.joelotter.com/posts/2019/12/games-played-2019"&gt;2019&lt;/a&gt;), but&#10;this year I thought I&amp;rsquo;d try and write a little bit about each one. This is in&#10;the order in which I played them, and includes a fair few games that didn&amp;rsquo;t come&#10;out in 2020.&lt;/p&gt;&#10;&lt;p&gt;It doesn&amp;rsquo;t include anything I bounced off immediately or played&#10;for less than an hour or so (unless it was short). It also doesn&amp;rsquo;t include&#10;anything I &lt;em&gt;started&lt;/em&gt; playing in a previous year - so although I played a lot of&#10;Persona 5 and Divinity this year, they don&amp;rsquo;t count.&lt;/p&gt;&#10;&lt;p&gt;In 2020, I played 56 games for the first time. Here they are.&lt;/p&gt;&#10;&lt;h4 id="disco-elysium-pc"&gt;Disco Elysium (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2020/12/disco.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Disco Elysium is one of the smartest games I have ever played. It made me feel&#10;like I haven&amp;rsquo;t read enough political theory or been to enough raves. It is&#10;extremely sharply written; it mocked me, it taught me, it made me quite angry&#10;and it brought me close to tears. One of the very finest RPGs ever made.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m the sorriest cop.&lt;/p&gt;&#10;&lt;h4 id="battletech-pc"&gt;BATTLETECH (PC)&lt;/h4&gt;&#10;&lt;p&gt;I think BATTLETECH might be a game that I enjoy hearing about more than actually&#10;playing it. Does it have big stompy mechs? Yes, it does. Is the setting&#10;interesting? It sure is. Do I have the patience to actually play it? No, but&#10;every time someone like Austin Walker or Rob Zacny tells a story about how a&#10;particular battle went, I&amp;rsquo;m hooked. I think this is a fantastic game, but not&#10;necessary a Joel-game.&lt;/p&gt;&#10;&lt;h4 id="lennas-inception-pc"&gt;Lenna&amp;rsquo;s Inception (PC)&lt;/h4&gt;&#10;&lt;p&gt;I didn&amp;rsquo;t play enough of this game to have a proper opinion on it, but it&amp;rsquo;s got a&#10;really clever hook and it&amp;rsquo;s often really charming. I mean to have another go.&lt;/p&gt;&#10;&lt;h4 id="murder-by-numbers-switch"&gt;Murder By Numbers (Switch)&lt;/h4&gt;&#10;&lt;p&gt;I knew from one look at the aesthetic of this game, and knowing that Ed Fear&#10;worked on it, that I&amp;rsquo;d enjoy it. What I didn&amp;rsquo;t expect was how challenging it&#10;gets. Turns out, Picross can get &lt;em&gt;really&lt;/em&gt; hard. Haven&amp;rsquo;t finished it yet, but&#10;intend to.&lt;/p&gt;&#10;&lt;h4 id="gears-of-war-ultimate-pc"&gt;Gears of War Ultimate (PC)&lt;/h4&gt;&#10;&lt;p&gt;I never played Gears back in the 360 days, so went into this with the&#10;expectation that it&amp;rsquo;d be a big brash shooter featuring some massive ham men and&#10;a rubbish plot. That&amp;rsquo;s exactly what it is, but what surprised me was that it&amp;rsquo;s&#10;also a fairly smart shooter, putting an emphasis on sightlines and flanking and&#10;even some basic tactics in a way that its peers don&amp;rsquo;t. Enjoyed it.&lt;/p&gt;&#10;&lt;h4 id="ori-and-the-will-of-the-wisps-pc"&gt;Ori and the Will of the Wisps (PC)&lt;/h4&gt;&#10;&lt;p&gt;I liked the first Ori quite a bit. I also &lt;em&gt;love&lt;/em&gt; Hollow Knight. It seems like a&#10;fair few folks at Moon Studios share my view, but unfortunately the result feels&#10;like imitation rather than inspiration. The movement is still super sharp and I&#10;do quite like the faintly overwrought emotional beats, but it reminded me too&#10;much of a game I would rather be playing.&lt;/p&gt;&#10;&lt;h4 id="shenzen-io-pc"&gt;SHENZEN I/O (PC)&lt;/h4&gt;&#10;&lt;p&gt;This is a funny one - I enjoyed the main bit of this fake assembly programming&#10;game quite a bit, but haven&amp;rsquo;t actually engaged with it much because after a day&#10;of work programming the last thing I want to do is more programming. However,&#10;the included solitaire game is a blast, and has become my go-to &amp;ldquo;I got five&#10;minutes while my thing is in the oven&amp;rdquo; game. If you&amp;rsquo;re not into the programming&#10;stuff at all I think the solitaire game is available standalone.&lt;/p&gt;&#10;&lt;h4 id="tangle-tower-ios"&gt;Tangle Tower (iOS)&lt;/h4&gt;&#10;&lt;p&gt;Like Neo Cab last year, Tangle Tower proved to be another excellent Apple Arcade&#10;game with a rubbish ending. It&amp;rsquo;s not so bad that I wouldn&amp;rsquo;t recommend the game,&#10;but when the rest of it is so charming and wonderfully crafted it was a bit&#10;of a disappointment to see it not stick the landing. The voice acting in this&#10;game is sublime.&lt;/p&gt;&#10;&lt;h4 id="control-foundation-dlc-pc"&gt;Control: Foundation DLC (PC)&lt;/h4&gt;&#10;&lt;p&gt;I loved Control, so More Control sounded great. This DLC didn&amp;rsquo;t do much more&#10;than that, but I didn&amp;rsquo;t really need it to.&lt;/p&gt;&#10;&lt;h4 id="animal-crossing-new-horizons-switch"&gt;Animal Crossing New Horizons (Switch)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2020/12/acnh.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Animal Crossing came out right near the start of the lockdown and I leaped on&#10;it like so many of us did. What do I need to tell you? It&amp;rsquo;s Animal Crossing,&#10;with the addition of some crafting mechanics I find mildly tedious and seasonal&#10;events that I utterly adore. Lot of folks raced to get the best island as&#10;quickly as possible and burned themselves out on it, but I play these games very&#10;slowly and I imagine I&amp;rsquo;ll be popping back to the island town of Lighthouse for&#10;many years to come.&lt;/p&gt;&#10;&lt;h4 id="dragon-age-origins-pc"&gt;Dragon Age Origins (PC)&lt;/h4&gt;&#10;&lt;p&gt;This one was a replay, kind of, but given the PC version is so different to the&#10;Xbox 360 version I bounced off so many years ago I think it&amp;rsquo;s OK.&lt;/p&gt;&#10;&lt;p&gt;The DNA of Neverwinter Nights is clear, which made me happy, and the tactical&#10;combat is pretty deep, though playing it right off the back of Divinity Original&#10;Sin 2 was never going to let it shine. As ever with this era of BioWare, it&amp;rsquo;s&#10;the character writing and worldbuilding that justifiably steals the show.&lt;/p&gt;&#10;&lt;h4 id="in-other-waters-switch"&gt;In Other Waters (Switch)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2020/12/eow.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I didn&amp;rsquo;t play enough of this one yet to have a strong opinion on it, but &lt;em&gt;look&lt;/em&gt;&#10;at it. It&amp;rsquo;s gorgeous. It&amp;rsquo;s also mechanically unique with a very cool setting.&lt;/p&gt;&#10;&lt;h4 id="final-fantasy-7-switch"&gt;Final Fantasy 7 (Switch)&lt;/h4&gt;&#10;&lt;p&gt;I never played Final Fantasy 7 back in the day - wasn&amp;rsquo;t a PlayStation kid - so&#10;the release of the remaster (remake?) seemed like a good time to visit the&#10;original as preparation.&lt;/p&gt;&#10;&lt;p&gt;I never ended up playing the remake, but I got past the point it gets to in FF7&#10;and a little bit further. What I discovered was a game that has aged extremely&#10;well, despite being first-generation polygonal, with an interesting story and&#10;some battle mechanics I quite liked. Will revisit.&lt;/p&gt;&#10;&lt;h4 id="elite-dangerous-pc"&gt;Elite Dangerous (PC)&lt;/h4&gt;&#10;&lt;p&gt;I had a go at this in VR and found it pretty enjoyable, though its controller&#10;prompts when playing on Oculus are just&amp;hellip;wrong. The level of detail here is&#10;astounding, and I wasn&amp;rsquo;t able to give it the time it deserves - I&amp;rsquo;ll probably&#10;revisit once the big update arrives.&lt;/p&gt;&#10;&lt;h4 id="half-life-alyx-pc"&gt;Half-Life Alyx (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2020/12/hla.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;My excellent and clever friend &lt;a href="https://ggapp.io/ob"&gt;Oliver&lt;/a&gt; said in his review:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;Revelatory. This must be what it felt like to play Doom in 1993.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;I agree. Half-Life Alyx does for VR what Super Mario 64 did for the third&#10;dimension - it simultaneously justifies the existence of the medium while&#10;making everything else seem like a tech demo. It&amp;rsquo;s the best VR game that exists,&#10;and by an extremely wide margin. I&amp;rsquo;ve barely touched my Quest since I finished&#10;this game and there&amp;rsquo;s not been anything since that&amp;rsquo;s made me consider sticking&#10;it back on the charger.&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s not just a great VR game, it&amp;rsquo;s a full-grown Half-Life game, with everything&#10;that entails. The world is richly realised, the sound design is impeccable, and&#10;the ending left me, a long-time fan of this series, reeling. The addition of&#10;Rhys Darby is an inspired choice. After so long existing mostly as a platform&#10;holder (I do not care about DOTA and neither should you) it&amp;rsquo;s wonderful to see&#10;Valve doing what they do best. It even made me feel a little bit less sad about&#10;the fact we&amp;rsquo;ll likely never play the next Campo Santo game. A little.&lt;/p&gt;&#10;&lt;h4 id="yakuza-0-pc"&gt;Yakuza 0 (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2020/12/yakuza.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Much has been made of how uniquely daft this series is - do karaoke after&#10;running a real estate business while beating up hitmen! - but all I&amp;rsquo;d been told&#10;didn&amp;rsquo;t quite capture just how good the story is, how believable the characters,&#10;how &lt;em&gt;hateful&lt;/em&gt; Kuze. I&amp;rsquo;ve been playing this one in little bits throughout the&#10;year and it&amp;rsquo;s got me completely hooked. I can&amp;rsquo;t but imagine how much of my life&#10;this series is going to consume and I don&amp;rsquo;t resent it one bit.&lt;/p&gt;&#10;&lt;h4 id="wattam-pc"&gt;Wattam (PC)&lt;/h4&gt;&#10;&lt;p&gt;You get to hold hands with a toilet, a tooth and a big nose, and then make&#10;everything explode. It&amp;rsquo;s delightful nonsense. The controls are nonsense too.&lt;/p&gt;&#10;&lt;h4 id="star-wars-jedi-fallen-order-pc"&gt;Star Wars Jedi Fallen Order (PC)&lt;/h4&gt;&#10;&lt;p&gt;I heard a lot of comparisons to Souls games about this one and I can see where&#10;they come from. Jedi Fallen Order is not a good Souls game. What it is, however,&#10;is a really good Ratchet and Clank. No further questions.&lt;/p&gt;&#10;&lt;h4 id="opus-magnum-pc"&gt;Opus Magnum (PC)&lt;/h4&gt;&#10;&lt;p&gt;I think there&amp;rsquo;s something about Zachtronics games - they&amp;rsquo;re so well-designed I&#10;wish I&amp;rsquo;d made them, and when I have my game designer hat on they&amp;rsquo;re a&#10;bottomless source of inspiration, but I find it so hard to actually &lt;em&gt;play&lt;/em&gt; them.&#10;I think it&amp;rsquo;s because these days I play games to unwind. Opus Magnum, like other&#10;Zachtronics titles, requires your brain to be fully engaged.&lt;/p&gt;&#10;&lt;p&gt;I want to revisit this one. The few hours I put into it were great.&lt;/p&gt;&#10;&lt;h4 id="slay-the-spire-pc"&gt;Slay the Spire (PC)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;d been eyeing this one from afar for quite some time - it&amp;rsquo;s made using the&#10;same tech as my own game, Feud, and I&amp;rsquo;ve always wondered how they managed to do&#10;some of this stuff with LibGDX.&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s a great game, too. I&amp;rsquo;m very bad at it.&lt;/p&gt;&#10;&lt;h4 id="umurangi-generation-pc"&gt;Umurangi Generation (PC)&lt;/h4&gt;&#10;&lt;p&gt;There&amp;rsquo;s been a lot written about this one, far better than I could. I think&#10;we&amp;rsquo;ll look back on Umurangi Generation as one of the most important games of its&#10;generation. It makes a couple of mechanical missteps (that timer really needs to&#10;go) but I can fully understand why it&amp;rsquo;s so many folks&amp;rsquo; game of the year.&lt;/p&gt;&#10;&lt;p&gt;There&amp;rsquo;s not a huge amount I can say about this game without spoiling some aspect&#10;of what it is and does. Play it.&lt;/p&gt;&#10;&lt;h4 id="ape-out-pc"&gt;Ape Out (PC)&lt;/h4&gt;&#10;&lt;p&gt;You know the film Birdman? With the drums? This is Birdman but instead of a&#10;washed-up superhero actor you&amp;rsquo;re a big ape.&lt;/p&gt;&#10;&lt;p&gt;I may not have fully thought this comparison through but I&amp;rsquo;m going with it.&lt;/p&gt;&#10;&lt;h4 id="switch-n-shoot-pc"&gt;Switch n Shoot (PC)&lt;/h4&gt;&#10;&lt;p&gt;One of those game concepts that&amp;rsquo;s so simple, so elegant, that you&amp;rsquo;re cross at&#10;yourself for not thinking of it first. It&amp;rsquo;s also fantastically well-executed. I&#10;just wish there was an iOS port.&lt;/p&gt;&#10;&lt;h4 id="the-last-of-us-part-2-ps4"&gt;The Last of Us Part 2 (PS4)&lt;/h4&gt;&#10;&lt;p&gt;Yeah, I played it. There&amp;rsquo;s so much Discourse around this game I&amp;rsquo;m hesitant to&#10;add to the pile.&lt;/p&gt;&#10;&lt;p&gt;One thing I hear a lot is that it&amp;rsquo;s a misery simulator, relentlessly grim, you&#10;feel like shit after playing it. This is entirely true, but I think that&amp;rsquo;s it&#10;basically succeeding at what it sets out to do.&lt;/p&gt;&#10;&lt;p&gt;There&amp;rsquo;s so much could be said about its commentary on violence and retribution,&#10;on relationships, on trauma, but I&amp;rsquo;m not going to say any of that because other&#10;people - like my friend &lt;a href="https://www.patreon.com/posts/last-of-us-part-38075642"&gt;Sam&lt;/a&gt; - have done that much better.&lt;/p&gt;&#10;&lt;p&gt;What I will say is: I think the core game loop is really quite dull. You play as&#10;a homicidal noo-noo, hoovering up bits of tape and scissors until you can stab&#10;another boy in the neck for getting between you and the information you need to&#10;go and find the next place to hoover. It also ends with a tedious fistfight with&#10;bad signposting and clunky controls and fail states, because it&amp;rsquo;s a Naughty Dog&#10;game and they apparently still think that&amp;rsquo;s how games need to end.&lt;/p&gt;&#10;&lt;p&gt;I really hope there&amp;rsquo;s no Part 3, but I think we all know there will be.&lt;/p&gt;&#10;&lt;h4 id="creaks-ios"&gt;Creaks (iOS)&lt;/h4&gt;&#10;&lt;p&gt;It&amp;rsquo;s an Amanita game, so beautiful design work is pretty much a given, but&#10;Creaks still made me pause and just &lt;em&gt;look&lt;/em&gt;. It&amp;rsquo;s also a craftily clever wee&#10;puzzle platformer, a genre I had thought I was completely burned out on.&lt;/p&gt;&#10;&lt;h4 id="necrobarista-ios"&gt;Necrobarista (iOS)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2020/12/necrobarista.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;One of the most interesting games I played this year; it&amp;rsquo;s more of a graphic&#10;novel than anything else, and the more interactive bits where you walk around&#10;the coffee shop are the least interesting part. It manages to explore death and&#10;acceptance of mortality in a really interesting way, and it&amp;rsquo;s grounded by some&#10;of the best writing and most convincing characters I experienced this year.&lt;/p&gt;&#10;&lt;p&gt;I cannot recommend this one enough - but play it on PC, the iOS version runs at&#10;a single-digit framerate in many places.&lt;/p&gt;&#10;&lt;h4 id="return-of-the-obra-dinn-switch"&gt;Return of the Obra Dinn (Switch)&lt;/h4&gt;&#10;&lt;p&gt;I started playing it, put it down, and on my return realised I had no idea what&#10;was going on. Obra Dinn gives you a &lt;em&gt;lot&lt;/em&gt; to fit in your head - links between&#10;people, places, and events all have to be managed in some kind of monochrome&#10;mind palace or they&amp;rsquo;ll slip through the cracks completely and can be very hard&#10;to dig out again.&lt;/p&gt;&#10;&lt;p&gt;I finished it on my second go through and loved it, though I will admit to&#10;looking up one answer to check I had it right.&lt;/p&gt;&#10;&lt;h4 id="wide-ocean-big-jacket-pc"&gt;Wide Ocean Big Jacket (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2020/12/wobj.jpeg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Wide Ocean Big Jacket is about two hours long and it&amp;rsquo;s perfect. The writing is&#10;stellar - I actually winced a couple of times at how familiar the conversations&#10;of the two teenaged characters felt - and it doesn&amp;rsquo;t try to overreach, something&#10;I actually found quite refreshing in a game like this. It sits within its quiet&#10;moments in a really lovely way, and I&amp;rsquo;m still thinking about it now.&lt;/p&gt;&#10;&lt;h4 id="spiritfarer-switch"&gt;Spiritfarer (Switch)&lt;/h4&gt;&#10;&lt;p&gt;Spiritfarer has a lovely setup and concept - pick up the souls of the dead, look&#10;after them for a bit, tend to their needs, eventually help them move on. A&#10;perfect tend-and-befriend thing, I thought, and entirely suitable for the&#10;hellish year it launched in.&lt;/p&gt;&#10;&lt;p&gt;Unfortunately, the process of looking after these itinerant spirits is basically&#10;a never-ending list of boring chores, with the ungrateful bastards constantly&#10;demanding more blankets or a nicer house or popcorn. I played for about ten&#10;hours and not a single one of these pricks left the boat. So I did.&lt;/p&gt;&#10;&lt;h4 id="bird-alone-ios"&gt;Bird Alone (iOS)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2020/12/birdalone.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;At first glance this looks like a gorgeously illustrated tamagotchi, but once&#10;you&amp;rsquo;re into it you&amp;rsquo;ll realise it&amp;rsquo;s a really beautiful meditation on growing&#10;older. This one had a big emotional impact on me. Can&amp;rsquo;t say much more without&#10;spoiling it.&lt;/p&gt;&#10;&lt;p&gt;One of my very favourite games of 2020.&lt;/p&gt;&#10;&lt;h4 id="hypnospace-outlaw-pc"&gt;Hypnospace Outlaw (PC)&lt;/h4&gt;&#10;&lt;p&gt;This is a game I didn&amp;rsquo;t give enough time, but I intend to remedy that next year.&#10;Fantastically inventive with a deep insight into what a lost version of the&#10;internet was like, it also features writing from Xalavier Nelson Jr., the&#10;funniest person working in games today.&lt;/p&gt;&#10;&lt;h4 id="dead-cells-xbox"&gt;Dead Cells (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;I do love a good roguelike on occasion, and Dead Cells is a very good roguelike.&#10;The combat is snappy and the run-loop is satisfying, if a little bit&#10;unforgiving. I&amp;rsquo;m intrigued by its setting, but it doesn&amp;rsquo;t really seem like it&amp;rsquo;s&#10;going to use it much more than brief flavour. Maybe that&amp;rsquo;s good. Maybe not every&#10;game needs a lore wiki as required reading.&lt;/p&gt;&#10;&lt;h4 id="journey-to-the-savage-planet-xbox"&gt;Journey to the Savage Planet (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;Had a go at this one on Game Pass and I&amp;rsquo;m very glad I didn&amp;rsquo;t have to spend&#10;money on it. The &amp;ldquo;humour&amp;rdquo; is so very, very tiring.&lt;/p&gt;&#10;&lt;h4 id="alan-wake-pc"&gt;Alan Wake (PC)&lt;/h4&gt;&#10;&lt;p&gt;This was a replay ahead of the Control AWE DLC, but I last played it in 2010 or&#10;something so I&amp;rsquo;m going to let this count.&lt;/p&gt;&#10;&lt;p&gt;Alan Wake has mostly aged pretty well - the godawful facial animation was bad&#10;even at the time, and the drudgery of the last third of the game is exactly how&#10;I remember it. The rest of it&amp;rsquo;s pretty OK, though.&lt;/p&gt;&#10;&lt;h4 id="carto-xbox"&gt;Carto (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2020/12/carto.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Carto looks very cute - and it is - but I had to look up some of the solutions&#10;to puzzles. It&amp;rsquo;s not easy, and some of the solutions veer a little too far into&#10;the how-would-I-have-ever-thought-of-that adventure game style, but it&amp;rsquo;s a&#10;charming adventure and well worth your time.&lt;/p&gt;&#10;&lt;h4 id="doom-eternal-xbox"&gt;Doom Eternal (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;I finally got around to finishing DOOM 2016 this year and loved it. It was&#10;so refreshingly free of bloat. It knew exactly what it was, and didn&amp;rsquo;t try to be&#10;more. Despite being a game where a massive tin man saws demons in half with a&#10;chainsaw, I&amp;rsquo;d describe it as &amp;rsquo;elegant'.&lt;/p&gt;&#10;&lt;p&gt;Doom Eternal learns all the wrong lessons from its predecessor. Right from the&#10;jump it overwhelms with extra systems, mechanics and collectibles. Its plot, UI&#10;and controls are bewildering. It&amp;rsquo;s a technical triumph, but that can&amp;rsquo;t distract&#10;from its shortcomings. A disappointment.&lt;/p&gt;&#10;&lt;h4 id="a-monsters-expedition-ios"&gt;A Monster&amp;rsquo;s Expedition (iOS)&lt;/h4&gt;&#10;&lt;p&gt;I really like A Monster&amp;rsquo;s Expedition - the puzzles are pitched perfectly, enough&#10;to be brain ticklers but never tough enough you&amp;rsquo;d get really stuck. There&amp;rsquo;s also&#10;some really lovely writing which had me chuckling out loud a few times. Worth a&#10;look.&lt;/p&gt;&#10;&lt;h4 id="mass-effect-3-pc"&gt;Mass Effect 3 (PC)&lt;/h4&gt;&#10;&lt;p&gt;Another replay from a long time ago, wrapping up my trilogy replay I started&#10;last year. I&amp;rsquo;ve long thought that Mass Effect 3 was unfairly maligned and this&#10;replay has only strengthened that feeling. Certain missions in particular play&#10;so well into the &amp;ldquo;the gang&amp;rsquo;s all here&amp;rdquo; trope that I find really soothing. The&#10;Leviathan DLC is a particular highlight.&lt;/p&gt;&#10;&lt;h4 id="i-am-dead-pc"&gt;I Am Dead (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2020/12/iamdead.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I Am Dead is joyous. Its setting and characters are so vibrant and its core&#10;mechanic is a work of technical and conceptual genius. It really charmed me, and&#10;brought me a great deal of comfort at a time when I really, really needed it.&lt;/p&gt;&#10;&lt;p&gt;The fish people love toast because it is so dry!!! Play this game, I beg.&lt;/p&gt;&#10;&lt;h4 id="control-awe-dlc-pc"&gt;Control: AWE DLC (PC)&lt;/h4&gt;&#10;&lt;p&gt;I made the mistake of playing this one in the dark. Bad idea. I&amp;rsquo;ve not got very&#10;far yet. Someday I&amp;rsquo;ll be brave enough.&lt;/p&gt;&#10;&lt;p&gt;On a semi-related note, this was the first game I played after upgrading to the&#10;RTX 3070. The ray-tracing is a nice thing to have, sure, but I think DLSS will&#10;prove to be the real game-changer. In the industry. Not the game. Obviously&#10;already the game.&lt;/p&gt;&#10;&lt;h4 id="star-wars-squadrons-pc"&gt;Star Wars Squadrons (PC)&lt;/h4&gt;&#10;&lt;p&gt;It&amp;rsquo;s no Alyx, but this game really is improved by playing it in VR - you can&#10;look around the cockpit freely, which is particularly helpful in the Rebel ships&#10;as they have windows that extend over your head. It feels very cool spotting an&#10;incoming TIE fighter by glancing upwards while pulling off a tricky manoeuvre.&lt;/p&gt;&#10;&lt;h4 id="among-us-pc"&gt;Among Us (PC)&lt;/h4&gt;&#10;&lt;p&gt;You already know about this one.&lt;/p&gt;&#10;&lt;p&gt;The first time I played this I said I&amp;rsquo;d never played it, and everyone assumed it&#10;was some kind of trick. It wasn&amp;rsquo;t. I still won, somehow. Triple-bluff? I&amp;rsquo;ve lost&#10;count.&lt;/p&gt;&#10;&lt;h4 id="black-mesa-pc"&gt;Black Mesa (PC)&lt;/h4&gt;&#10;&lt;p&gt;I never played the original Half-Life, and the final completion of this remake&#10;seemed like as good a time as any. The remake itself is extremely well done; if&#10;you told me Valve had made it themselves I&amp;rsquo;d believe you without question.&lt;/p&gt;&#10;&lt;p&gt;Sometimes these kinds of remake can expose aged game design, but this one&#10;only serves to highlight the strength of the original game. I&amp;rsquo;m not very far yet&#10;but I&amp;rsquo;m really enjoying it.&lt;/p&gt;&#10;&lt;h4 id="blasphemous-pc"&gt;Blasphemous (PC)&lt;/h4&gt;&#10;&lt;p&gt;This one was a little overhyped for me. I went in expecting the best attempt at&#10;a 2D soulslike yet. It&amp;rsquo;s pretty great, I love the aesthetic, and the Spanish dub&#10;lends it so much flavour. But it didn&amp;rsquo;t measure up to what I had in my head for&#10;it. Shame.&lt;/p&gt;&#10;&lt;h4 id="hades-switch"&gt;Hades (Switch)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2020/12/hades.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I still haven&amp;rsquo;t managed to escape, but I&amp;rsquo;m having a great time with this one&#10;nonetheless. Supergiant have long been one of my favourite studios, and this&#10;might be their finest work to date - it feels incredibly responsive in the&#10;hands, the writing is top-notch and it&amp;rsquo;s by far the most forgiving and rewarding&#10;roguelike loop I&amp;rsquo;ve ever seen.&lt;/p&gt;&#10;&lt;p&gt;Just gotta kill that bastard Theseus.&lt;/p&gt;&#10;&lt;h4 id="destiny-2-beyond-light-pc"&gt;Destiny 2: Beyond Light (PC)&lt;/h4&gt;&#10;&lt;p&gt;Bungie deleted half of the game and somehow I like it more.&lt;/p&gt;&#10;&lt;p&gt;This was a big year of Destiny for me. I joined a clan, started running raids,&#10;managed to nab the Chronicler seal after months of work and completed a season&#10;pass for the first time.&lt;/p&gt;&#10;&lt;p&gt;Beyond Light manages to clear Destiny&amp;rsquo;s cobwebs away, and remove a fair chunk of&#10;the game, without it feeling like my progress up to that point wasn&amp;rsquo;t valued. I&#10;do miss a couple of areas - I think the Titan Arcology was criminally&#10;underused - but in all but name this feels like Destiny 3, and I&amp;rsquo;m having a&#10;fantastic time with it.&lt;/p&gt;&#10;&lt;p&gt;Runs like a dream on the PS5, too.&lt;/p&gt;&#10;&lt;h4 id="if-found-ios"&gt;If Found&amp;hellip; (iOS)&lt;/h4&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2020/12/if_found.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;If Found&amp;hellip; is stunning. Visually beautiful, mechanically inspired and the story&#10;hit me like a black hole to the stomach. It&amp;rsquo;s also set in Ireland, as if it&#10;wasn&amp;rsquo;t already perfect.&lt;/p&gt;&#10;&lt;p&gt;Probably my game of the year. Play it.&lt;/p&gt;&#10;&lt;h4 id="minecraft-dungeons-xbox"&gt;Minecraft Dungeons (Xbox)&lt;/h4&gt;&#10;&lt;p&gt;I reconnected with some old friends via Xbox Live this year, and what we really&#10;needed was a really great &amp;ldquo;something to do with your hands while your brain is&#10;in 2010&amp;rdquo; game. Minecraft Dungeons fit the bill perfectly.&lt;/p&gt;&#10;&lt;h4 id="red-dead-redemption-2-pc"&gt;Red Dead Redemption 2 (PC)&lt;/h4&gt;&#10;&lt;p&gt;I&amp;rsquo;ve only played a few hours of this. It took me three goes to get through the&#10;intro, and it&amp;rsquo;s a Rockstar game so it controls like a tank that&amp;rsquo;s been out on&#10;the lash, but the sad cowboys have already stolen my heart.&lt;/p&gt;&#10;&lt;h4 id="bugsnax-pc"&gt;Bugsnax (PC)&lt;/h4&gt;&#10;&lt;p&gt;I thought this game was a glorified meme but it&amp;rsquo;s a delight. There&amp;rsquo;s some lovely&#10;emergent systemic interaction going on, but more importantly, the noises the&#10;Snax make are &lt;em&gt;adorable&lt;/em&gt;. Strabby strabby strabby.&lt;/p&gt;&#10;&lt;h4 id="octodad-dadliest-catch-pc"&gt;Octodad: Dadliest Catch (PC)&lt;/h4&gt;&#10;&lt;p&gt;&lt;em&gt;*approving bloop*&lt;/em&gt;&lt;/p&gt;&#10;&lt;h4 id="astros-playroom-ps5"&gt;Astro&amp;rsquo;s Playroom (PS5)&lt;/h4&gt;&#10;&lt;p&gt;It&amp;rsquo;s basically a big ad for the PlayStation brand, but it&amp;rsquo;s so charming and&#10;polished that it knocked the cynicism right out of my mouth. It&amp;rsquo;s not&#10;necessarily something I&amp;rsquo;d pay money for but it&amp;rsquo;s easily the best console pack-in&#10;since Wii Sports.&lt;/p&gt;&#10;&lt;h4 id="spider-man-remastered-ps5"&gt;Spider-Man Remastered (PS5)&lt;/h4&gt;&#10;&lt;p&gt;I bounced off Insomniac&amp;rsquo;s Spider-Man pretty hard back when it launched in 2018 -&#10;the open-world bloat felt excessive, like an Arkham game. When I got the PS5 I&#10;decided to give it another chance, and I ended up really loving it. The trick is&#10;that, though the side stuff is largely quite dull, the game doesn&amp;rsquo;t punish you&#10;in any way for ignoring it.&lt;/p&gt;&#10;&lt;p&gt;I think this game and Into The Spider-Verse get Peter Parker&amp;rsquo;s Spider-Man right&#10;in a way that the MCU films do not - and I know why. I&amp;rsquo;ve nailed it down.&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;Peter Parker calls baddies by their first name.&lt;/em&gt;&lt;/p&gt;&#10;&lt;h4 id="rise-of-the-tomb-raider-ps4"&gt;Rise of the Tomb Raider (PS4)&lt;/h4&gt;&#10;&lt;p&gt;It&amp;rsquo;s very visually impressive and I enjoyed the couple of hours I spent with it.&#10;I&amp;rsquo;ve heard &lt;em&gt;very&lt;/em&gt; good things about this one and I don&amp;rsquo;t think I&amp;rsquo;m far enough in&#10;to understand why, yet.&lt;/p&gt;&#10;&lt;h4 id="spider-man-miles-morales-ps5"&gt;Spider-Man: Miles Morales (PS5)&lt;/h4&gt;&#10;&lt;p&gt;I love Miles as a character. He gets to fulfil the childhood dreams of anyone&#10;who grew up with Spider-Man, but I can&amp;rsquo;t bring myself to resent him because he&amp;rsquo;s&#10;so likeable, so geeky, so honest.&lt;/p&gt;&#10;&lt;p&gt;The game isn&amp;rsquo;t doing much new, but it doesn&amp;rsquo;t need to.&lt;/p&gt;&#10;&lt;h4 id="ni-no-kuni-wrath-of-the-white-witch-switch"&gt;Ni No Kuni Wrath of the White Witch (Switch)&lt;/h4&gt;&#10;&lt;p&gt;I haven&amp;rsquo;t played very much of this yet but I would already die for Drippy.&lt;/p&gt;&#10;&lt;h4 id="demons-souls-ps5"&gt;Demon&amp;rsquo;s Souls (PS5)&lt;/h4&gt;&#10;&lt;p&gt;I never had a PS3 so missed out on this one the first time around. Demon&amp;rsquo;s Souls&#10;is unforgiving in a way that I think the later games eased up on a little bit -&#10;the enemies and bosses are by themselves not too overwhelming, but it&amp;rsquo;s miserly&#10;with shortcuts and runs to bosses can be very lengthy.&lt;/p&gt;&#10;&lt;p&gt;It was nice when Tower Knight went down on the second, attempt, though, and I&#10;think it&amp;rsquo;s a testament to how faithful Bluepoint have been to From&amp;rsquo;s design that&#10;my muscle memory from other games was immediately transferable.&lt;/p&gt;&#10;&lt;h4 id="hitman-ps4"&gt;HITMAN (PS4)&lt;/h4&gt;&#10;&lt;p&gt;I put rat poison in a guy&amp;rsquo;s wine and drowned him in the toilet when he went to&#10;throw up, 11/10.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;Some year. Let&amp;rsquo;s see what 2021 brings. Hopefully a full Feud release, for my&#10;part.&lt;/p&gt;</content:encoded></item><item><title>COVID-19 app incompatibility points at a larger issue in tech</title><link>https://www.joelotter.com/posts/2020/09/ios-covid-tracking/</link><pubDate>Thu, 24 Sep 2020 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2020/09/ios-covid-tracking/</guid><description>&lt;p&gt;After six months and £11m of public money wasted, England and Wales finally have&#10;a functional contact tracing app for COVID-19. You can download it now - as long&#10;as your phone is new enough. Why this limitation?&lt;/p&gt;</description><content:encoded>&lt;p&gt;After six months and £11m of public money wasted, England and Wales finally have&#10;a functional contact tracing app for COVID-19. You can download it now - as long&#10;as your phone is new enough. Why this limitation?&lt;/p&gt;&#10;&lt;p&gt;The answer is, as ever: it&amp;rsquo;s complicated. Or, if you&amp;rsquo;re as tired as I am: money.&#10;Let&amp;rsquo;s back up a bit first, though.&lt;/p&gt;&#10;&lt;h2 id="how-did-we-get-here"&gt;How did we get here?&lt;/h2&gt;&#10;&lt;p&gt;Back in the olden times - early 2020, a decade ago - the UK government announced&#10;they were creating a track &amp;amp; trace app for COVID-19. Around the same time, Apple&#10;and Google announced that they would be adding functionality to their respective&#10;mobile operating systems to enable decentralised, peer-to-peer proximity&#10;tracking.&lt;/p&gt;&#10;&lt;p&gt;For reasons that are still not really understood, the UK decided it would &lt;em&gt;not&lt;/em&gt;&#10;be using this API, and would instead build their own solution from scratch. I&#10;can only assume no software engineers were &lt;a href="https://www.youtube.com/watch?v=BKorP55Aqvg"&gt;consulted&lt;/a&gt; during this process; attempting to do this kind of local Bluetooth-based&#10;tracking on iOS as a background process is not possible due to limitations&#10;imposed by the operating system itself. Users on iPhone would need to be walking&#10;around with their phone out and unlocked, with the app running, in order to&#10;participate in the tracking.&lt;/p&gt;&#10;&lt;p&gt;Of course, the government decided to plough ahead with this plan, stating&#10;somewhat bizarrely that they would be relying on an &lt;a href="https://www.theguardian.com/world/2020/may/06/critical-mass-of-android-users-needed-for-success-of-nhs-coronavirus-contact-tracing-app"&gt;&amp;ldquo;Android herd immunity&amp;rdquo;&lt;/a&gt; strategy in order for track and trace to succeed. The development of this&#10;patently unusable software set the taxpayer back &lt;a href="https://www.standard.co.uk/news/uk/test-and-trace-app-cost-uk-government-11million-a4474386.html"&gt;£11 million&lt;/a&gt;. If this had happened at a competent software company, everyone involved would&#10;have been fired or possibly shot before the first line of code was written.&lt;/p&gt;&#10;&lt;p&gt;Eventually, inevitably, the government made a &lt;a href="https://www.theguardian.com/world/2020/jun/18/uk-poised-to-abandon-coronavirus-app-in-favour-of-apple-and-google-models"&gt;U-turn&lt;/a&gt; (an increasingly familiar term), stating that they would now in fact use&#10;the Apple/Google approach, but the app would now not launch in May as planned.&lt;/p&gt;&#10;&lt;p&gt;So here we are, in late September, and the app is finally here.&lt;/p&gt;&#10;&lt;h2 id="ios-135"&gt;iOS 13.5&lt;/h2&gt;&#10;&lt;blockquote class="twitter-tweet" data-theme="light"&gt;&lt;p lang="en" dir="ltr"&gt;Tried to download the &lt;a href="https://twitter.com/NHSCOVID19app?ref_src=twsrc%5Etfw"&gt;@NHSCovid19app&lt;/a&gt; on my phone, an iPhone 6. Won&amp;#39;t work. &amp;quot;This application requires iOS 13.5 or later.&amp;quot; So, only people with phones less than three years old can use it. This is what the Tories call a &amp;quot;world-beating&amp;quot; system. Well done, &lt;a href="https://twitter.com/MattHancock?ref_src=twsrc%5Etfw"&gt;@MattHancock&lt;/a&gt;, you genius.&lt;/p&gt;&amp;mdash; Simon Price (@simon_price01) &lt;a href="https://twitter.com/simon_price01/status/1309020725043437568?ref_src=twsrc%5Etfw"&gt;September 24, 2020&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;&#10;&lt;p&gt;A lot of folks on the Hell Website are understandably upset that the new app&#10;won&amp;rsquo;t run on older phones - iOS (the operating system, or OS, that iPhones run)&#10;13.5 is available only from the iPhone 6S&#10;onwards, a phone that released in 2015. It&amp;rsquo;s easy to dismiss this as government&#10;incompetence, but the problem runs deeper than this.&lt;/p&gt;&#10;&lt;p&gt;The iOS app relies on Apple&amp;rsquo;s &lt;a href="https://developer.apple.com/documentation/exposurenotification"&gt;Exposure Notification&lt;/a&gt; API, which was introduced in iOS 13.5, released earlier this year. iOS apps&#10;targeting older versions of the OS cannot use this API at all.&lt;/p&gt;&#10;&lt;p&gt;Features being added in new software versions is par for the course. However, a&#10;typical solution to allow access to older devices is the process known as&#10;&lt;strong&gt;backporting&lt;/strong&gt;. Apple could, if they so chose, release a new version of iOS 12&#10;featuring the new API; something like iOS 12.5. This isn&amp;rsquo;t so far-fetched as one&#10;might assume - the latest version of iOS 12, 12.4.8, was released in July 2020,&#10;&lt;em&gt;after&lt;/em&gt; 13.5.&lt;/p&gt;&#10;&lt;p&gt;We can only speculate as to Apple&amp;rsquo;s reasons for not doing this, but the problem&#10;is exacerbated by the fact that iOS is so strictly device-limited. The story in&#10;Android land is quite different.&lt;/p&gt;&#10;&lt;p&gt;The Android &lt;a href="https://developers.google.com/android/exposure-notifications/exposure-notifications-api"&gt;Exposure Notifications API&lt;/a&gt; supports Android 6.0 onwards, with some support also offered for 5.0. These OS&#10;versions launched in 2015 and 2014 respectively, so are of a similar age to the&#10;poor neglected iPhone 6. It seems Android is targeting devices supporting&#10;Bluetooth Low Energy, or BLE; this standard was introduced in Bluetooth 4.0,&#10;and is supported from iOS 5 onwards - an operating system from 2011.&lt;/p&gt;&#10;&lt;p&gt;Android&amp;rsquo;s fragmentation issues are well-documented, and some manufacturers do&#10;stop updating their devices faster than they should. However, the more open&#10;nature of this operating system means that a user can, with a little work,&#10;install a newer version of the OS on their device of choice. This isn&amp;rsquo;t possible&#10;on iOS, even via jailbreak. The Android team also have a commitment to backwards&#10;compatibility which borders on the obsessive - a necessity when Android is so&#10;widespread. New Android APIs are usually backported to older versions of the OS,&#10;meaning users are not left behind.&lt;/p&gt;&#10;&lt;h2 id="why-dont-apple-do-this"&gt;Why don&amp;rsquo;t Apple do this?&lt;/h2&gt;&#10;&lt;p&gt;Apple want you to buy iPhones. They don&amp;rsquo;t want you to buy &lt;em&gt;an&lt;/em&gt; iPhone. They want&#10;you to effectively subscribe to a form of lock-in where you pay an entry fee to&#10;a walled garden whose walls grow higher every September.&lt;/p&gt;&#10;&lt;p&gt;I really like my iPhone, but Apple&amp;rsquo;s update model means that in a few years I&#10;will no longer be able to upgrade the operating system. A few years after that&#10;I won&amp;rsquo;t be able to receive app updates, as their minimum iOS version is&#10;continually on the rise. Even the underlying chipset changes every few years,&#10;with forward compatibility being &lt;a href="https://www.zdnet.com/article/get-ready-for-the-ios-11-appocalypse/"&gt;essentially nil&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;The decision to install a piece of software on a device really ought to belong&#10;to the person who paid for said device. Android understands this - rooting my&#10;ancient HTC Desire to install Android 5.0 voided my warranty, sure, but it was&#10;easy to do. There&amp;rsquo;s no such solution on iOS, and if someone were to discover&#10;one, Apple would surely move to block it.&lt;/p&gt;&#10;&lt;p&gt;This is known as &amp;ldquo;&lt;strong&gt;planned obsolescence&lt;/strong&gt;&amp;rdquo; and you&amp;rsquo;ve probably heard of it.&#10;You might already associate it with Apple - its &lt;a href="https://en.wikipedia.org/wiki/Planned_obsolescence"&gt;Wikipedia page&lt;/a&gt; features a picture of an iPhone. Planned obsolescence is anti-consumer, making&#10;us spend more money on products that don&amp;rsquo;t last us as long as they could, but it&#10;has deeper ramifications. The production of electronics is bad for the&#10;environment, processor manufacturing is often &lt;a href="https://www.latimes.com/opinion/op-ed/la-oe-merchant-iphone-supplychain-20170723-story.html"&gt;dependant&lt;/a&gt; on conflict minerals and exploitation,&#10;and the piles of electronic waste that cannot be recycled grow ever higher.&lt;/p&gt;&#10;&lt;p&gt;With the Exposure Notification API, we&amp;rsquo;re seeing a very real example of&#10;planned obsolescence impacting a public health crisis. The hope is that enough&#10;people in the UK will be able to install this app for it to have a useful&#10;effect, but when &lt;a href="https://www.theguardian.com/world/2020/sep/24/take-up-of-nhs-contact-tracing-app-could-be-only-10"&gt;uptake already looks low&lt;/a&gt; the locking out of older devices, which likely are perfectly capable at the&#10;hardware level of running this software, is discouraging.&lt;/p&gt;</content:encoded></item><item><title>Notarizing Java applications on macOS</title><link>https://www.joelotter.com/posts/2020/08/macos-java-notarization/</link><pubDate>Fri, 14 Aug 2020 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2020/08/macos-java-notarization/</guid><description>&lt;p&gt;Notarization is required as of macOS 10.15 (Catalina), and it&amp;rsquo;s a bit of a&#10;minefield - doubly so for a Java application, or anything built outside Xcode.&lt;/p&gt;</description><content:encoded>&lt;p&gt;Notarization is required as of macOS 10.15 (Catalina), and it&amp;rsquo;s a bit of a&#10;minefield - doubly so for a Java application, or anything built outside Xcode.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;d seen a few good &lt;a href="https://www.molleindustria.org/blog/notarizing-your-flashair-applications-for-macos/"&gt;guides&lt;/a&gt;&#10;around the place explaining&#10;how to get this working in non-typical build environments, but Java had its own&#10;quirks I needed to deal with. This focuses on&#10;&lt;a href="https://bearwaves.itch.io/feud"&gt;Feud&lt;/a&gt;, my LibGDX game, but should be applicable&#10;to most Java applications.&lt;/p&gt;&#10;&lt;h3 id="what-is-notarization"&gt;What is notarization?&lt;/h3&gt;&#10;&lt;p&gt;If you&amp;rsquo;re reading this I&amp;rsquo;m assuming it&amp;rsquo;s specifically because you want to&#10;notarize something but just in case, let&amp;rsquo;s go over the basics.&lt;/p&gt;&#10;&lt;p&gt;Apple has a piece of software on macOS called&#10;&lt;a href="https://support.apple.com/en-us/HT202491"&gt;Gatekeeper&lt;/a&gt;. This is designed (in&#10;theory) to prevent malicious software from being accidentally run on macOS.&#10;That&amp;rsquo;s why you&amp;rsquo;ll sometimes see the &amp;ldquo;This software is from an unidentified&#10;source&amp;rdquo; popup when trying to run something not downloaded from the Mac App&#10;Store. Fine.&lt;/p&gt;&#10;&lt;p&gt;In 10.15, Gatekeeper introduced the &lt;em&gt;additional&lt;/em&gt; requirement that any piece of&#10;software you want to run must be notarized - the developer has sent it off to&#10;Apple, Apple have looked at it and gone &amp;ldquo;yes mate this is legit&amp;rdquo; and handed&#10;it back to the developer, who then puts a special &amp;ldquo;Apple approved&amp;rdquo; stamp on the&#10;app before uploading it to wherever. This is a change from 10.14 where this&#10;requirement only existed for Mac App Store apps. There&amp;rsquo;s a way for users to&#10;get around this (detailed in the Gatekeeper link) but it&amp;rsquo;s tedious, and they&amp;rsquo;d&#10;run into issues when launching an app through a launcher like Steam.&lt;/p&gt;&#10;&lt;p&gt;The TL;DR of all of this is that if you want to sensibly distribute macOS&#10;software you now need to notarize it. Sorry.&lt;/p&gt;&#10;&lt;h3 id="what-do-i-need"&gt;What do I need?&lt;/h3&gt;&#10;&lt;p&gt;A big pile of money! No, but in all seriousness:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;An Apple developer license. Yes, the $100/year one. Sorry.&lt;/li&gt;&#10;&lt;li&gt;A Mac running 10.13.7 (High Sierra) or later.&lt;/li&gt;&#10;&lt;li&gt;Xcode&lt;/li&gt;&#10;&lt;li&gt;Your app to be 64-bit. If you aren&amp;rsquo;t compiling to 64-bit in the year of our&#10;lord 2020 I am a bit concerned. If you&amp;rsquo;re trying to notarize an old app, good&#10;news! - it won&amp;rsquo;t work on Catalina regardless, as 32-bit support has been&#10;&lt;a href="https://support.apple.com/en-gb/HT208436"&gt;dropped&lt;/a&gt;. Sorry, again.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Once you&amp;rsquo;ve jumped through these hoops, we can commence jumping through the&#10;other seven thousand hoops.&lt;/p&gt;&#10;&lt;h3 id="building-and-packaging"&gt;Building and packaging&lt;/h3&gt;&#10;&lt;p&gt;This step will be a little different for everyone. We want to produce a &lt;strong&gt;.app&lt;/strong&gt;&#10;file that bundles a JRE. I&amp;rsquo;ll describe my process for a LibGDX app, but if&#10;you&amp;rsquo;re using something else or your application isn&amp;rsquo;t a game at all, here are&#10;some broad requirements:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Any binaries must link against at least the macOS 10.9 SDK.&lt;/li&gt;&#10;&lt;li&gt;You must enable the hardened runtime, and ship a JRE that uses the hardened&#10;runtime. I use AdoptOpenJDK, which supports it on the newest versions (I use&#10;&lt;code&gt;8u252-b09.1&lt;/code&gt;).&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;To package my LibGDX game as a .app file, I use&#10;&lt;a href="https://github.com/libgdx/packr"&gt;packr&lt;/a&gt;. Unfortunately the mainline packr repo&#10;appears to be mostly unmaintained at the time of writing, so I&amp;rsquo;ve been using&#10;&lt;a href="https://github.com/karlsabo/packr"&gt;karlsabo&amp;rsquo;s fork&lt;/a&gt;. Please go and star that,&#10;they&amp;rsquo;re doing god&amp;rsquo;s work keeping the tool maintained.&lt;/p&gt;&#10;&lt;p&gt;Here&amp;rsquo;s my incantation. Note that I use LWJGL3, hence &lt;code&gt;-XstartOnFirstThread&lt;/code&gt;.&#10;YMMV.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;java -jar packr.jar &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --platform mac &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --jdk jdk_mac.zip &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --executable feud &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --classpath feud_desktop.jar &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --mainclass com.bearwaves.feud.desktop.DesktopLauncher &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --vmargs Xmx2G XstartOnFirstThread &lt;span class="s2"&gt;&amp;#34;Dsun.java2d.dpiaware=true&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --icon desktop/icons.icns &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --minimizejre ./buildTools/minimise.json &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --output build/desktop/mac/Feud.app&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should replace those values with your own where relevant. If you&amp;rsquo;re on Java&#10;8 like myself, the following minifier JSON should help (other platforms omitted&#10;for brevity):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;reduce&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;remove&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;platform&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;*&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;paths&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/bin/rmid&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/bin/rmiregistry&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/bin/tnameserv&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/bin/keytool&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/bin/policytool&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/bin/orbd&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/bin/servertool&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/bin/javaws&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/javaws.jar&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/jfr.jar&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/oblique-fonts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/desktop&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/plugin&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/THIRDPARTYLICENSEREADME-JAVAFX.txt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/ant-javafx.jar&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/javafx.properties&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/jfxswt.jar&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;platform&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;macos&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;paths&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/fxplugins.dylib&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/libdecora_sse.so&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/libdecora-sse.dylib&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/libfxplugins.so&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/libglass.dylib&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/libglib-lite.dylib&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/libgstreamer-lite.dylib&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/libjavafx_font_t2k.dylib&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/libjavafx-font.dylib&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/libjavafx-iio.dylib&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/libjfxmedia.dylib&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/libjfxwebkit.dylib&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/libprism_common.dylib&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/libprism_sw.dylib&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;jre/lib/libprism-es2.dylib&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Those on a newer Java like 11 will probably have a small enough JRE that&#10;minification ceases to matter.&lt;/p&gt;&#10;&lt;p&gt;However you end up doing it, you should finish this process with a .app. We&amp;rsquo;re&#10;not done yet. Not nearly.&lt;/p&gt;&#10;&lt;h3 id="signing"&gt;Signing&lt;/h3&gt;&#10;&lt;p&gt;Signing is the process of putting our fingerprint on every little binary in our&#10;application so that users can trust it&amp;rsquo;s actually from us and not some hacked&#10;version inserted by a bad actor.&lt;/p&gt;&#10;&lt;p&gt;We need to create a special certificate for this. Head over to the &lt;a href="https://developer.apple.com/account/resources/certificates/add"&gt;developer&#10;console&lt;/a&gt; and&#10;create a &lt;strong&gt;Developer ID Application&lt;/strong&gt; certificate. You&amp;rsquo;ll have to create a CSR&#10;from Keychain Access and upload it - the instructions on that page should see&#10;you through.&lt;/p&gt;&#10;&lt;p&gt;Got that? Load it into Keychain Access and copy the name - all of it. It&amp;rsquo;ll be&#10;something like&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Developer ID Application: YOUR COOL NAME (SHFJ28E7Q)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll also need to create a &lt;a href="https://developer.apple.com/account/resources/identifiers/list"&gt;macOS app identifier&lt;/a&gt; for your&#10;application. Use an explicit bundle ID and remember it. Mine is&#10;&lt;code&gt;com.bearwaves.feud.macos&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;We also need to create an &lt;a href="https://developer.apple.com/library/archive/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/AboutEntitlements.html"&gt;entitlements&lt;/a&gt; file. This is essentially a list of permissions we want Apple to grant our&#10;app. You can have mine for free:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE plist PUBLIC &amp;#34;-//Apple//DTD PLIST 1.0//EN&amp;#34; &amp;#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd&amp;#34;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;plist&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;1.0&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#9;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;com.apple.security.cs.allow-jit&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#9;&lt;span class="nt"&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#9;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;com.apple.security.cs.allow-unsigned-executable-memory&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#9;&lt;span class="nt"&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#9;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;com.apple.security.cs.disable-library-validation&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#9;&lt;span class="nt"&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#9;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;com.apple.security.cs.allow-dyld-environment-variables&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#9;&lt;span class="nt"&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you&amp;rsquo;re copying this please make sure to use tabs as indents rather than&#10;spaces, as I believe that&amp;rsquo;s actually a requirement.&lt;/p&gt;&#10;&lt;p&gt;We can start signing now. Jump into your terminal and head over to the directory&#10;where your &lt;strong&gt;.app&lt;/strong&gt; lives (if you don&amp;rsquo;t know how to use a terminal I cannot&#10;stress enough how useful it is).&lt;/p&gt;&#10;&lt;p&gt;We&amp;rsquo;re going to first clear all the signing information from the &lt;strong&gt;.app&lt;/strong&gt;, just&#10;in case you started following this guide halfway through.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;xattr -cr Feud.app&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Fresh and clean. Did you know that a &lt;strong&gt;.app&lt;/strong&gt; is actually a directory? You&amp;rsquo;re&#10;about to. In we go!&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; Feud.app&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What we&amp;rsquo;re going to do now is sign every single macOS binary inside the app&#10;bundle. These will be located in a few places:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Your actual app binary - probably in &lt;code&gt;Contents/MacOS&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;Any additional libraries or frameworks you&amp;rsquo;re shipping. I ship my own versions&#10;of libpng and libfreetype - these live in &lt;code&gt;Contents/Frameworks&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;The binaries inside the JAR. Packr places mine in&#10;&lt;code&gt;Contents/Resources/&lt;/code&gt;; your packaging tool may be different.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;You might be wondering about the JRE itself. I&amp;rsquo;ve found that as this is already&#10;codesigned by AdoptOpenJDK I don&amp;rsquo;t need to do that here.&lt;/p&gt;&#10;&lt;p&gt;I do all this stuff inside a shell script, and as such I&amp;rsquo;ve made a little&#10;&lt;code&gt;sign&lt;/code&gt; function you can use. If you&amp;rsquo;re not in a script you can just run the&#10;command against each binary.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;function&lt;/span&gt; sign&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# The DIST_CERT variable is the certificate name from earlier.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; codesign --force --options runtime --timestamp --sign &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$DIST_CERT&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --entitlements &lt;span class="s2"&gt;&amp;#34;path/to/my/entitlement.plist&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You might have seen in documentation somewhere that the &lt;code&gt;codesign&lt;/code&gt; tool has a&#10;&lt;code&gt;--deep&lt;/code&gt; flag. I tried using this in all its various permutations and was never&#10;able to get it to work, and the &lt;a href="https://developer.apple.com/library/archive/technotes/tn2206/_index.html#//apple_ref/doc/uid/DTS40007919-CH1-TNTAG404"&gt;advice&lt;/a&gt; seems to be not to use it. I&amp;rsquo;m not really sure what it&amp;rsquo;s for.&lt;/p&gt;&#10;&lt;p&gt;So, we need to sign every binary, &lt;em&gt;including&lt;/em&gt; the ones inside the JAR. To do&#10;this I just unzip the JAR, sign all the binaries I know about, and then zip it&#10;back up. The actual binaries that need signed will vary from application to&#10;application depending on what libraries you&amp;rsquo;ve included; basically it&amp;rsquo;s anything&#10;with a &lt;strong&gt;.dylib&lt;/strong&gt; extension. The script I have looks like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;pushd&lt;/span&gt; Feud.app/Contents/Resources&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir jar&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# platform is either &amp;#39;desktop&amp;#39; or &amp;#39;steam&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mv &lt;span class="s2"&gt;&amp;#34;feud_&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;platform&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.jar&amp;#34;&lt;/span&gt; jar/&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;pushd&lt;/span&gt; jar/&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;unzip &lt;span class="s2"&gt;&amp;#34;feud_&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;platform&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.jar&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm &lt;span class="s2"&gt;&amp;#34;feud_&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;platform&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.jar&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;dylibs&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;libopenal&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;libgdx64&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;libgdx-freetype64&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;libglfw&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;libjemalloc&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;liblwjgl&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;liblwjgl_opengl&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;platform&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;steam&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;dylibs&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;dylibs&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;libsteam_api&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;libsteamworks4j-encryptedappticket&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;libsteamworks4j-server&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;libsteamworks4j&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; lib in &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;dylibs&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# sign is the function a little up the page&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sign &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;lib&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.dylib&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;zip -r &lt;span class="s2"&gt;&amp;#34;../feud_&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;platform&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.jar&amp;#34;&lt;/span&gt; *&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;popd&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -rf jar/&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;popd&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once everything inside the &lt;strong&gt;.app&lt;/strong&gt; is signed, we should be able to validate it&#10;locally.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;codesign --verify --verbose&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Feud.app&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For my app, I get this output:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;--prepared:/Users/joel/Feud.app/Contents/Frameworks/libpng16.16.dylib&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;--validated:/Users/joel/Feud.app/Contents/Frameworks/libpng16.16.dylib&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;--prepared:/Users/joel/Feud.app/Contents/Frameworks/libfreetype.6.dylib&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;--validated:/Users/joel/Feud.app/Contents/Frameworks/libfreetype.6.dylib&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/Users/joel/Feud.app: valid on disk&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/Users/joel/Feud.app: satisfies its Designated Requirement&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hopefully your last line looks something similar. Unfortunately this doesn&amp;rsquo;t&#10;tell us it&amp;rsquo;s definitely correct, as codesign won&amp;rsquo;t look inside the JAR. This is&#10;the point where we have to send it up to Tim Apple and hope he&amp;rsquo;s feeling&#10;merciful.&lt;/p&gt;&#10;&lt;h3 id="notarizing"&gt;Notarizing&lt;/h3&gt;&#10;&lt;p&gt;This is it, the moment you&amp;rsquo;ve been waiting for, when your app gets to strut its&#10;stuff in front of the judges and you find out just how bad its singing is.&lt;/p&gt;&#10;&lt;p&gt;To do this we&amp;rsquo;re going to need what&amp;rsquo;s called an app-specific password. This is a&#10;single-use key that you can generate via your &lt;a href="https://appleid.apple.com/"&gt;Apple account&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Next, we need to zip up our &lt;strong&gt;.app&lt;/strong&gt; bundle (cos it&amp;rsquo;s a directory, remember?) and&#10;start the upload. I do something like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;zip -r Feud.app.zip Feud.app&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;xcrun altool -t osx -f Feud.app.zip &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --primary-bundle-id com.bearwaves.feud.macos --notarize-app &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --username me@example.com &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# APP_PASS is the single-use password you created&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --password &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$APP_PASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Apple aren&amp;rsquo;t going to give you the results right away; that would be too easy.&#10;Instead, they&amp;rsquo;ll eventually give you a UUID which you can use to check the&#10;status of your notarization request, like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;xcrun altool --notarization-info &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$UUID&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --username me@example.com &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --password &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$APP_PASS&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll have to wait a few minutes. You can rerun this command as many times as&#10;you like to check the status. Eventually you&amp;rsquo;ll either get a success message, or&#10;you&amp;rsquo;ll get a link to a bunch of logs explaining what went wrong. They&amp;rsquo;ll also&#10;email you. Yes, every time.&lt;/p&gt;&#10;&lt;p&gt;The failure logs are organised per-file and will tell you what&amp;rsquo;s wrong for each.&#10;The most likely ones you&amp;rsquo;ll get are:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;the binary isn&amp;rsquo;t signed, you must have missed it&lt;/li&gt;&#10;&lt;li&gt;the binary is linked against a too-old version of the macOS SDK&lt;/li&gt;&#10;&lt;li&gt;the binary doesn&amp;rsquo;t use the hardened runtime - you have a bad JRE, or forgot the&#10;&lt;code&gt;--options runtime&lt;/code&gt; flag&lt;/li&gt;&#10;&lt;li&gt;the binary doesn&amp;rsquo;t have a timestamp - you missed the &lt;code&gt;--timestamp&lt;/code&gt; flag&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;It&amp;rsquo;s usually enough to fix the offending binary, re-zip and re-upload.&lt;/p&gt;&#10;&lt;p&gt;Eventually you should get the all-clear, at which point we&amp;rsquo;re home and dry. The&#10;hardest part is over.&lt;/p&gt;&#10;&lt;h3 id="stapling"&gt;Stapling&lt;/h3&gt;&#10;&lt;p&gt;Stapling is basically taking the little receipt of goodness that Apple&#10;graciously bestowed upon us and attaching it to the app bundle. This just needs&#10;one command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;xcrun stapler staple Feud.app&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can check this worked like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;spctl --assess --verbose Feud.app&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you see something like this, congratulations; you&amp;rsquo;re done.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/Users/joel/Feud.app: accepted&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;source=Notarized Developer&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="the-end"&gt;The End&lt;/h3&gt;&#10;&lt;p&gt;This was an exhausting process to go through. I trawled through forum posts and&#10;had many late nights trying to get the damn thing to work on Java. I hope this&#10;post means you don&amp;rsquo;t.&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s been nearly a year since Catalina launched. Here&amp;rsquo;s hoping Apple don&amp;rsquo;t make&#10;any more massive changes to developer requirements on macOS any time soon!&lt;/p&gt;&#10;&lt;p&gt;What? &lt;a href="https://www.apple.com/uk/newsroom/2020/06/apple-announces-mac-transition-to-apple-silicon/"&gt;Oh for fuck&amp;rsquo;s sake&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title>The best days of my life</title><link>https://www.joelotter.com/posts/2020/08/the-best-days/</link><pubDate>Wed, 05 Aug 2020 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2020/08/the-best-days/</guid><description>&lt;p&gt;I finally got around to picking up Halo: The Master Chief Collection on PC. This&#10;was a very big step for me. Let me explain.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I finally got around to picking up Halo: The Master Chief Collection on PC. This&#10;was a very big step for me. Let me explain.&lt;/p&gt;&#10;&lt;p&gt;I haven&amp;rsquo;t touched Halo in years. Halo 4 didn&amp;rsquo;t do it for me and I never had an&#10;Xbox One, so the last time I spent any serious amount of time on this series&#10;would have been in the Halo: Reach days; the days before I left home and moved&#10;to London, away from my family, friends and everyone I knew. Since then, my life&#10;has had a very clear split.&lt;/p&gt;&#10;&lt;p&gt;Since moving to London - the years AL - it&amp;rsquo;s been software engineering, poetry,&#10;the games industry, paying bills, desperately trying to work out what the hell&#10;my opinions are and working out why the smell of damp in the bathroom won&amp;rsquo;t go&#10;away. AL stands for After London, but it could also stand for Adult Life.&lt;/p&gt;&#10;&lt;p&gt;Before London it was books, dreams of being a musician, family, Northern&#10;Ireland, school, God. Childhood. A sense of safety. Importantly, Halo.&lt;/p&gt;&#10;&lt;p&gt;Halo was the game around which my social life was structured. I&amp;rsquo;d play every&#10;single evening with Zach, Daniel and Ridwan. There were a bunch of others who&#10;would drop in from time to time, but the core group was C69, E12, C05 and Z24.&#10;We called ourselves Falls&amp;rsquo; Fantabulous Flying Four and, at least in my memory,&#10;we were &lt;em&gt;good&lt;/em&gt;. Halo was the first game whose multiplayer I fell into and it&#10;would take years for Destiny 2 to launch and for me to find that again.&lt;/p&gt;&#10;&lt;p&gt;We spent our evenings playing Halo. We spent school breaks and lunchtimes&#10;talking about Halo. When Reach came out, Zach sewed the patch that came with it&#10;onto his jacket. We referred to each other by our in-game ranks (I made Major)&#10;and we were only half joking. A weekend social activity might consist of going&#10;to someone&amp;rsquo;s house, drinking far too many fizzy drinks and playing Halo. Those&#10;times we put the effort in to bring Xboxes together for System Link are&#10;treasured memories indeed.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2020/08/halotdu.jpg" alt=""&gt;&#10;&lt;span class="caption"&gt;This isn&amp;rsquo;t even the same group of friends. This&#10;was just my life.&lt;/span&gt;&lt;/p&gt;&#10;&lt;p&gt;Around the time ODST came out, Falls, our glorious leader, became obsessed with&#10;acquiring all the Vidmaster achievements, which would apparently grant us the&#10;elusive-to-the-point-of-myth Recon armour. I can&amp;rsquo;t remember how many we ended&#10;up getting, but two stick out to me.&lt;/p&gt;&#10;&lt;p&gt;The first, Endure, was one we never finished. It required you to pass the fourth&#10;set on Firefight, Halo ODST&amp;rsquo;s horde mode, on the Heroic difficulty setting.&#10;We made many, many attempts. We had power outages. We had me go to bed leaving&#10;a hastily scrawled sign saying &amp;ldquo;Endure in progress, DO NOT SWITCH OFF&amp;rdquo; in front&#10;of the Xbox. On one occasion we were in the very last stretch and it was just&#10;me, a gravity hammer, and one too many brutes.&lt;/p&gt;&#10;&lt;p&gt;We &lt;em&gt;did&lt;/em&gt; manage to complete Annual. This required you to complete the final&#10;mission in Halo 3, in 4-player co-op, on the legendary difficulty, with the Iron&#10;skull on (one death = everyone dead) and everyone in Ghosts rather than the&#10;usual Warthog. We finished it despite an oft-recalled incident where my&#10;long-suffering mother managed to freeze our internet doing an online Tesco shop.&#10;For some reason the game entirely locked up, but we could still hear each other.&#10;Every network blip for the next several years would have the first&#10;troubleshooting step of &amp;ldquo;Joel, is your mum on Tesco?&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;Life started to get busier when I was 17. Many of my Halo friends left school,&#10;and without those lunchtime conversations the game didn&amp;rsquo;t have quite the same&#10;draw. Still, I played Reach a fair bit, and this became my main way to stay in&#10;touch with those people.&lt;/p&gt;&#10;&lt;p&gt;One weekend in the summer of 2012, before I moved away, Zach came round. We sat&#10;side by side and played through the Halo Reach campaign, start to finish. I&#10;remember us barely saying a word to each other. We didn&amp;rsquo;t need to. That was all&#10;the goodbye I needed.&lt;/p&gt;&#10;&lt;p&gt;I find nostalgia to be a curious and powerful force. Since I moved away I&amp;rsquo;ve&#10;been completely unable to play Halo. The sound of the main menu music is quite&#10;literally capable of reducing me to tears. Every time I visit home the&#10;temptation to dust off the 360 is there, but I can never quite bring myself to&#10;do it.&lt;/p&gt;&#10;&lt;p&gt;Today, I install MCC and load it up. A few tracks cycle as I explore the&#10;rabbit warren of menus, and then the one I&amp;rsquo;ve been waiting for arrives.&lt;/p&gt;&#10;&lt;div class="video"&gt;&#10; &lt;iframe src="https://www.youtube.com/embed/uXcWxw7UHME?start=46"&#10; frameborder="0"&#10; allow="accelerometer; autoplay; encrypted-media; gyroscope;&#10; picture-in-picture"&#10; allowfullscreen&gt;&#10; &lt;/iframe&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Suddenly, I&amp;rsquo;m 16 again. It&amp;rsquo;s a Sunday evening, I&amp;rsquo;ve just eaten toast with jam,&#10;and the Xbox headset is creating a line in my just-washed hair that will show up&#10;all day tomorrow. I&amp;rsquo;m not looking forward to school, but right now I&amp;rsquo;m playing&#10;the best game with my best friends and I am untouchable.&lt;/p&gt;&#10;&lt;p&gt;I go into the customisation menu and I choose the Recon armour. It feels right.&#10;Then I shoot Falls a message.&lt;/p&gt;</content:encoded></item><item><title>Speed Up Catalina With This One Weird Tip!</title><link>https://www.joelotter.com/posts/2020/07/speed-up-catalina/</link><pubDate>Thu, 30 Jul 2020 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2020/07/speed-up-catalina/</guid><description>&lt;p&gt;The newest version of macOS (well, that&amp;rsquo;s out) has caused a lot of issues for a lot of people, game developers especially. I&amp;rsquo;ll write about my notarization woes in a separate post, but this should help address quite a specific issue.&lt;/p&gt;</description><content:encoded>&lt;p&gt;The newest version of macOS (well, that&amp;rsquo;s out) has caused a lot of issues for a lot of people, game developers especially. I&amp;rsquo;ll write about my notarization woes in a separate post, but this should help address quite a specific issue.&lt;/p&gt;&#10;&lt;p&gt;The problem is detailed on this great article, &lt;a href="https://sigpipe.macromates.com/2020/macos-catalina-slow-by-design/"&gt;macOS 10.15: Slow by Design&lt;/a&gt;. Essentially, every time you execute a program for the first time, it sends &lt;em&gt;something&lt;/em&gt; off to Apple in order to analyse it according to Apple&amp;rsquo;s typically inscrutable security policies.&lt;/p&gt;&#10;&lt;p&gt;This sounds fine for regular users, but developers who work primarily on the command line will be executing programs constantly (thanks, &lt;a href="https://homepage.cs.uri.edu/~thenry/resources/unix_art/ch01s06.html"&gt;UNIX philosophy&lt;/a&gt;). This applies to user-written Shell scripts too. Security is important, but I&amp;rsquo;m not necessarily interested in Apple&amp;rsquo;s opinion on my bash script that mass-uploads cat images to Slack.&lt;/p&gt;&#10;&lt;p&gt;Thankfully there&amp;rsquo;s a solution, hinted at in the article but somewhat unclear due to Apple not really documenting it.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2020/07/privacy.png" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Buried in the privacy settings is this Developer Tools panel. Any executables added to this will in turn be able to execute programs while bypassing the mandatory check. I recommend adding your terminal as a minimum, along with anything else you think relevant - IDEs, game engine editors and so on. I&amp;rsquo;ve noticed a speed improvement in the order of &lt;em&gt;seconds&lt;/em&gt; in some cases of first execution.&lt;/p&gt;&#10;&lt;p&gt;The problem is that not everyone has this panel, and at Catalina&amp;rsquo;s launch it was unclear what was needed to make it unlock. It now appears that you need to install the latest version of Xcode.&lt;/p&gt;&#10;&lt;p&gt;I tried doing this right after upgrading to Catalina and got an extremely helpful &amp;ldquo;Failed&amp;rdquo; error message. It seems that unless you were on whatever the latest Xcode available to Mojave was (probably 11.1) the upgrade breaks the install. Great. Delete Xcode, reinstall it, you should have the panel now.&lt;/p&gt;&#10;&lt;p&gt;Doctors Hate Him.&lt;/p&gt;</content:encoded></item><item><title>Games I played in 2019</title><link>https://www.joelotter.com/posts/2019/12/games-played-2019/</link><pubDate>Tue, 31 Dec 2019 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2019/12/games-played-2019/</guid><description>&lt;p&gt;As has become a yearly tradition, here is my list of video games I played in 2019.&lt;/p&gt;&#10;&lt;p&gt;A sun (☀️) means I liked it a LOT and would recommend you at least have a look at it. Some rain (🌧) means it didn&amp;rsquo;t really do it for me or was disappointing in some way (I did not play any bad games this year). Anything without an emoji is Real Nice. A trophy (🏆) signifies my personal favourite game of the year.&lt;/p&gt;</description><content:encoded>&lt;p&gt;As has become a yearly tradition, here is my list of video games I played in 2019.&lt;/p&gt;&#10;&lt;p&gt;A sun (☀️) means I liked it a LOT and would recommend you at least have a look at it. Some rain (🌧) means it didn&amp;rsquo;t really do it for me or was disappointing in some way (I did not play any bad games this year). Anything without an emoji is Real Nice. A trophy (🏆) signifies my personal favourite game of the year.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Monster boy and the cursed kingdom (Switch)&lt;/li&gt;&#10;&lt;li&gt;Chuchel (iOS)&lt;/li&gt;&#10;&lt;li&gt;Assassin&amp;rsquo;s creed origins (PC)&lt;/li&gt;&#10;&lt;li&gt;The Haunted Island, a Frog Detective Game (PC) ☀️&lt;/li&gt;&#10;&lt;li&gt;Heaven Will Be Mine (iOS)&lt;/li&gt;&#10;&lt;li&gt;Pikuniku (Switch) ☀️&lt;/li&gt;&#10;&lt;li&gt;Enderal (PC)&lt;/li&gt;&#10;&lt;li&gt;Star fox 64 3D (3DS)&lt;/li&gt;&#10;&lt;li&gt;Tearaway (Vita)&lt;/li&gt;&#10;&lt;li&gt;Lumines Electronic Symphony (Vita)&lt;/li&gt;&#10;&lt;li&gt;Divekick (Vita)&lt;/li&gt;&#10;&lt;li&gt;Twinfold (iOS) ☀️&lt;/li&gt;&#10;&lt;li&gt;DMC5 (PC)&lt;/li&gt;&#10;&lt;li&gt;Baba Is You (Switch) ☀️&lt;/li&gt;&#10;&lt;li&gt;Thomas Was Alone (PC)&lt;/li&gt;&#10;&lt;li&gt;Metro Exodus (PC)&lt;/li&gt;&#10;&lt;li&gt;Sunless Skies (PC) ☀️🏆&lt;/li&gt;&#10;&lt;li&gt;Wargroove (Switch)&lt;/li&gt;&#10;&lt;li&gt;Heaven&amp;rsquo;s Vault (PS4) ☀️&lt;/li&gt;&#10;&lt;li&gt;Fugl (PC)&lt;/li&gt;&#10;&lt;li&gt;Wandersong (PC) ☀️&lt;/li&gt;&#10;&lt;li&gt;Divinity: Original Sin 2 (PC) ☀️&lt;/li&gt;&#10;&lt;li&gt;Outer Wilds (PC) ☀️&lt;/li&gt;&#10;&lt;li&gt;Florence (iOS)&lt;/li&gt;&#10;&lt;li&gt;Monster Hunter 4 Ultimate (3DS)&lt;/li&gt;&#10;&lt;li&gt;Observation (PC)&lt;/li&gt;&#10;&lt;li&gt;Hades (PC) ☀️&lt;/li&gt;&#10;&lt;li&gt;Superhot VR (Quest)&lt;/li&gt;&#10;&lt;li&gt;Fire Emblem: Three Houses (Switch)&lt;/li&gt;&#10;&lt;li&gt;Zero Escape 999 (PC)&lt;/li&gt;&#10;&lt;li&gt;Dicey Dungeons (PC)&lt;/li&gt;&#10;&lt;li&gt;A Short Hike (PC) ☀️&lt;/li&gt;&#10;&lt;li&gt;Control (PC)&lt;/li&gt;&#10;&lt;li&gt;Vader Immortal episode 1 (Quest)&lt;/li&gt;&#10;&lt;li&gt;Beat Saber (Quest)&lt;/li&gt;&#10;&lt;li&gt;Yoku&amp;rsquo;s Island Express (PC) ☀️&lt;/li&gt;&#10;&lt;li&gt;Untitled Goose Game (Switch) ☀️&lt;/li&gt;&#10;&lt;li&gt;Link’s Awakening (Switch)&lt;/li&gt;&#10;&lt;li&gt;What The Golf? (iOS)&lt;/li&gt;&#10;&lt;li&gt;Sky (iOS)&lt;/li&gt;&#10;&lt;li&gt;Sayonara Wild Hearts (iOS)&lt;/li&gt;&#10;&lt;li&gt;Over The Alps (iOS)&lt;/li&gt;&#10;&lt;li&gt;Destiny 2: Shadowkeep (PC)&lt;/li&gt;&#10;&lt;li&gt;Paratopic (PC) 🌧&lt;/li&gt;&#10;&lt;li&gt;Neo Cab (iOS) 🌧&lt;/li&gt;&#10;&lt;li&gt;Steamworld Dig (Switch)&lt;/li&gt;&#10;&lt;li&gt;Pokemon Sword (Switch)&lt;/li&gt;&#10;&lt;li&gt;Kind Words (PC) ☀️&lt;/li&gt;&#10;&lt;li&gt;Remnant: From the Ashes (PC)&lt;/li&gt;&#10;&lt;li&gt;Frog Detective 2 (PC) ☀️&lt;/li&gt;&#10;&lt;li&gt;Katana Zero (Switch)&lt;/li&gt;&#10;&lt;li&gt;Death Stranding (PS4) ☀️&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="some-notes"&gt;Some notes&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Sunless Skies is breathtaking in ambition and it almost consistently sticks the landing.&lt;/li&gt;&#10;&lt;li&gt;I really loved Neo Cab until the ending which is a big wet flop of a disappointment.&lt;/li&gt;&#10;&lt;li&gt;I did not get what Paratopic was trying to do but lots of people love it so if you have an interest in surreal horror stuff do check it out.&lt;/li&gt;&#10;&lt;li&gt;There are a few games on this list that might become suns when I play them more, such as MH4U, Remnant, Enderal or Pokemon Sword.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</content:encoded></item><item><title>Games I played in 2018</title><link>https://www.joelotter.com/posts/2018/12/games-played-2018/</link><pubDate>Mon, 31 Dec 2018 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2018/12/games-played-2018/</guid><description>&lt;p&gt;I don&amp;rsquo;t do game-of-the-year lists now because games are so varied it&amp;rsquo;s nigh impossible to compare two games from different genres. Here is a list of every game I played for the first time in 2018. Like last year:&lt;/p&gt;&#10;&lt;p&gt;🏆 - my personal favourite game this year&lt;br&gt;&#10;☀️ - loved it, recommend without hesitation&lt;br&gt;&#10;🌧 - didn&amp;rsquo;t really like it/felt like a chore to pay&lt;br&gt;&#10;everything else - pretty damn good, give it a go if your taste swings that way.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I don&amp;rsquo;t do game-of-the-year lists now because games are so varied it&amp;rsquo;s nigh impossible to compare two games from different genres. Here is a list of every game I played for the first time in 2018. Like last year:&lt;/p&gt;&#10;&lt;p&gt;🏆 - my personal favourite game this year&lt;br&gt;&#10;☀️ - loved it, recommend without hesitation&lt;br&gt;&#10;🌧 - didn&amp;rsquo;t really like it/felt like a chore to pay&lt;br&gt;&#10;everything else - pretty damn good, give it a go if your taste swings that way.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Gorogoa (PC) ☀️&lt;/li&gt;&#10;&lt;li&gt;Wolfenstein 2 (PC)&lt;/li&gt;&#10;&lt;li&gt;Subsurface Circular (PC)&lt;/li&gt;&#10;&lt;li&gt;The Norwood Suite (PC) ☀️&lt;/li&gt;&#10;&lt;li&gt;Shadow of the Colossus (PS4)&lt;/li&gt;&#10;&lt;li&gt;Celeste (Switch) ☀️&lt;/li&gt;&#10;&lt;li&gt;Stories Untold (PC) ☀️&lt;/li&gt;&#10;&lt;li&gt;Life is Strange (PC)&lt;/li&gt;&#10;&lt;li&gt;Into the Breach (PC/Switch)&lt;/li&gt;&#10;&lt;li&gt;Teleglitch (PC)&lt;/li&gt;&#10;&lt;li&gt;Metro 2033 (PC)&lt;/li&gt;&#10;&lt;li&gt;Metro Last Light (PC)&lt;/li&gt;&#10;&lt;li&gt;Else heart break (PC) 🌧&lt;/li&gt;&#10;&lt;li&gt;The Signal from Tolva (PC)&lt;/li&gt;&#10;&lt;li&gt;Receiver (PC) 🌧&lt;/li&gt;&#10;&lt;li&gt;Beeswing (PC) ☀️&lt;/li&gt;&#10;&lt;li&gt;Rime (PS4) 🌧&lt;/li&gt;&#10;&lt;li&gt;Minit (PC)&lt;/li&gt;&#10;&lt;li&gt;Jalopy (PC) 🌧&lt;/li&gt;&#10;&lt;li&gt;AER (PC)&lt;/li&gt;&#10;&lt;li&gt;Deus Ex: Mankind Divided (PC)&lt;/li&gt;&#10;&lt;li&gt;Donkey Kong Tropical Freeze (Switch)&lt;/li&gt;&#10;&lt;li&gt;Pokémon Quest (Switch/iOS) 🌧&lt;/li&gt;&#10;&lt;li&gt;God of War (PS4) ☀️&lt;/li&gt;&#10;&lt;li&gt;Hollow Knight (Switch) 🏆&lt;/li&gt;&#10;&lt;li&gt;Shadowrun Returns (PC)&lt;/li&gt;&#10;&lt;li&gt;Hellblade VR (Rift)&lt;/li&gt;&#10;&lt;li&gt;Rez Infinite (Rift)&lt;/li&gt;&#10;&lt;li&gt;Owlboy (Switch)&lt;/li&gt;&#10;&lt;li&gt;Okami HD (Switch)&lt;/li&gt;&#10;&lt;li&gt;Destiny 2 Forsaken (PC) ☀️&lt;/li&gt;&#10;&lt;li&gt;Spider-Man (PS4)&lt;/li&gt;&#10;&lt;li&gt;Windlands 2 (Rift) 🌧&lt;/li&gt;&#10;&lt;li&gt;Hot Tin Roof: The Cat Wore A Fedora (PC)&lt;/li&gt;&#10;&lt;li&gt;Little Nightmares (PC)&lt;/li&gt;&#10;&lt;li&gt;Q.U.B.E. (PC)&lt;/li&gt;&#10;&lt;li&gt;Heat Signature (PC) ☀️&lt;/li&gt;&#10;&lt;li&gt;Forza Horizon 4 (PC) ☀️&lt;/li&gt;&#10;&lt;li&gt;Kentucky Route Zero (PC)&lt;/li&gt;&#10;&lt;li&gt;Vanquish (PC) 🌧&lt;/li&gt;&#10;&lt;li&gt;Accounting+ (Rift)&lt;/li&gt;&#10;&lt;li&gt;Deltarune (PC)&lt;/li&gt;&#10;&lt;li&gt;Gone Home (PC)&lt;/li&gt;&#10;&lt;li&gt;Slime Rancher (PC) 🌧&lt;/li&gt;&#10;&lt;li&gt;Tetris Effect (PS4) ☀️&lt;/li&gt;&#10;&lt;li&gt;Quadrilateral Cowboy (PC)&lt;/li&gt;&#10;&lt;li&gt;GNOG (iOS)&lt;/li&gt;&#10;&lt;li&gt;Sunless Sea (iOS) ☀️&lt;/li&gt;&#10;&lt;li&gt;Donut County (iOS)&lt;/li&gt;&#10;&lt;li&gt;Quarantine Circular (Switch) ☀️&lt;/li&gt;&#10;&lt;li&gt;Mini Metro (iOS) ☀️&lt;/li&gt;&#10;&lt;li&gt;Moonlighter (Switch) ☀️&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</content:encoded></item><item><title>Games I played in 2017</title><link>https://www.joelotter.com/posts/2017/12/games-played-2017/</link><pubDate>Sun, 31 Dec 2017 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2017/12/games-played-2017/</guid><description>&lt;p&gt;Here follows the list of all the games I played in 2017, not including the ones I only played for a few minutes or the ones I bounced straight off.&lt;/p&gt;&#10;&lt;p&gt;A sun (☀️) means I have very strong positive feelings about the game; a rainy cloud (🌧️) means I have some negative or mixed feelings. Everything else is Pretty Darn Nice.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Final Fantasy XV (PS4, 2016) ☀️&lt;/li&gt;&#10;&lt;li&gt;The Last Guardian (PS4, 2016) 🌧️&lt;/li&gt;&#10;&lt;li&gt;Overwatch (PS4, 2016)&lt;/li&gt;&#10;&lt;li&gt;BOTW (Switch, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Shovel Knight (Switch, 2017)&lt;/li&gt;&#10;&lt;li&gt;Fast RMX (Switch, 2017)&lt;/li&gt;&#10;&lt;li&gt;Snake Pass (Switch, 2017)&lt;/li&gt;&#10;&lt;li&gt;Nier Automata (Switch, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;The Sexy Brutale (PS4, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Wonderboy: The Dragon&amp;rsquo;s Trap (Switch, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Thumper (Switch, 2017)&lt;/li&gt;&#10;&lt;li&gt;Tomb Raider (PC, 2013)&lt;/li&gt;&#10;&lt;li&gt;Elder Scrolls Online (PC, 2014)&lt;/li&gt;&#10;&lt;li&gt;ABZU (PC, 2016)&lt;/li&gt;&#10;&lt;li&gt;Bioshock (PC, 2007) ☀️&lt;/li&gt;&#10;&lt;li&gt;Horizon Zero Dawn (PS4, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Dishonored (PC, 2012) ☀️&lt;/li&gt;&#10;&lt;li&gt;GoNNer (Switch, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Kamiko (Switch, 2017)&lt;/li&gt;&#10;&lt;li&gt;Ori and the Blind Forest (PC, 2015)&lt;/li&gt;&#10;&lt;li&gt;PUBG (PC, 2017)&lt;/li&gt;&#10;&lt;li&gt;Virginia (PC, 2016)&lt;/li&gt;&#10;&lt;li&gt;What Remains of Edith Finch (PC, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Wolfenstein: The New Order (PC, 2014)&lt;/li&gt;&#10;&lt;li&gt;Pyre (PC, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Brothers: A Tale of Two Sons (PC, 2013)&lt;/li&gt;&#10;&lt;li&gt;No Man&amp;rsquo;s Sky (PC, 2016)&lt;/li&gt;&#10;&lt;li&gt;Pony Island (PC, 2016)&lt;/li&gt;&#10;&lt;li&gt;Mario Rabbids: Kingdom Battle (Switch, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Destiny 2 (PS4, 2017)&lt;/li&gt;&#10;&lt;li&gt;Fire Emblem Awakening (3DS, 2012)&lt;/li&gt;&#10;&lt;li&gt;Professor Layton and The Curious Village (DS, 2007)&lt;/li&gt;&#10;&lt;li&gt;Torment: Tides of Numenera (PC, 2017)&lt;/li&gt;&#10;&lt;li&gt;Hob (PC, 2017) 🌧️&lt;/li&gt;&#10;&lt;li&gt;Nuclear Throne (PC, 2015)&lt;/li&gt;&#10;&lt;li&gt;Devil Daggers (PC, 2016)&lt;/li&gt;&#10;&lt;li&gt;Hellblade: Senua&amp;rsquo;s Sacrifice (PC, 2017) 🌧️&lt;/li&gt;&#10;&lt;li&gt;Night in the Woods (PC, 2017) ☀️ (Probably my favourite game of 2017)&lt;/li&gt;&#10;&lt;li&gt;Pokemon Ultra Sun (3DS, 2017)&lt;/li&gt;&#10;&lt;li&gt;Super Mario Odyssey (Switch, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Flinthook (PC, 2017) 🌧️&lt;/li&gt;&#10;&lt;li&gt;Her Story (PC, 2015) ☀️&lt;/li&gt;&#10;&lt;li&gt;Dishonored 2 (PC, 2016)&lt;/li&gt;&#10;&lt;li&gt;Undertale (PC, 2015) ☀️&lt;/li&gt;&#10;&lt;li&gt;Thirty Flights of Loving (PC, 2012)&lt;/li&gt;&#10;&lt;li&gt;Everything (PC, 2017)&lt;/li&gt;&#10;&lt;li&gt;Tooth and Tail (PC, 2017)&lt;/li&gt;&#10;&lt;li&gt;Witcher 2: Assassins of Kings (PC, 2011) ☀️&lt;/li&gt;&#10;&lt;li&gt;Reigns: Her Majesty (iOS, 2017)&lt;/li&gt;&#10;&lt;li&gt;Chasing Birds (PC, 2017)&lt;/li&gt;&#10;&lt;li&gt;Dujanah (PC, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Persona 5 (PC, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Final Fantasy XII (PC, 2017)&lt;/li&gt;&#10;&lt;li&gt;Golf Story (Switch, 2017)&lt;/li&gt;&#10;&lt;/ul&gt;</description><content:encoded>&lt;p&gt;Here follows the list of all the games I played in 2017, not including the ones I only played for a few minutes or the ones I bounced straight off.&lt;/p&gt;&#10;&lt;p&gt;A sun (☀️) means I have very strong positive feelings about the game; a rainy cloud (🌧️) means I have some negative or mixed feelings. Everything else is Pretty Darn Nice.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Final Fantasy XV (PS4, 2016) ☀️&lt;/li&gt;&#10;&lt;li&gt;The Last Guardian (PS4, 2016) 🌧️&lt;/li&gt;&#10;&lt;li&gt;Overwatch (PS4, 2016)&lt;/li&gt;&#10;&lt;li&gt;BOTW (Switch, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Shovel Knight (Switch, 2017)&lt;/li&gt;&#10;&lt;li&gt;Fast RMX (Switch, 2017)&lt;/li&gt;&#10;&lt;li&gt;Snake Pass (Switch, 2017)&lt;/li&gt;&#10;&lt;li&gt;Nier Automata (Switch, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;The Sexy Brutale (PS4, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Wonderboy: The Dragon&amp;rsquo;s Trap (Switch, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Thumper (Switch, 2017)&lt;/li&gt;&#10;&lt;li&gt;Tomb Raider (PC, 2013)&lt;/li&gt;&#10;&lt;li&gt;Elder Scrolls Online (PC, 2014)&lt;/li&gt;&#10;&lt;li&gt;ABZU (PC, 2016)&lt;/li&gt;&#10;&lt;li&gt;Bioshock (PC, 2007) ☀️&lt;/li&gt;&#10;&lt;li&gt;Horizon Zero Dawn (PS4, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Dishonored (PC, 2012) ☀️&lt;/li&gt;&#10;&lt;li&gt;GoNNer (Switch, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Kamiko (Switch, 2017)&lt;/li&gt;&#10;&lt;li&gt;Ori and the Blind Forest (PC, 2015)&lt;/li&gt;&#10;&lt;li&gt;PUBG (PC, 2017)&lt;/li&gt;&#10;&lt;li&gt;Virginia (PC, 2016)&lt;/li&gt;&#10;&lt;li&gt;What Remains of Edith Finch (PC, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Wolfenstein: The New Order (PC, 2014)&lt;/li&gt;&#10;&lt;li&gt;Pyre (PC, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Brothers: A Tale of Two Sons (PC, 2013)&lt;/li&gt;&#10;&lt;li&gt;No Man&amp;rsquo;s Sky (PC, 2016)&lt;/li&gt;&#10;&lt;li&gt;Pony Island (PC, 2016)&lt;/li&gt;&#10;&lt;li&gt;Mario Rabbids: Kingdom Battle (Switch, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Destiny 2 (PS4, 2017)&lt;/li&gt;&#10;&lt;li&gt;Fire Emblem Awakening (3DS, 2012)&lt;/li&gt;&#10;&lt;li&gt;Professor Layton and The Curious Village (DS, 2007)&lt;/li&gt;&#10;&lt;li&gt;Torment: Tides of Numenera (PC, 2017)&lt;/li&gt;&#10;&lt;li&gt;Hob (PC, 2017) 🌧️&lt;/li&gt;&#10;&lt;li&gt;Nuclear Throne (PC, 2015)&lt;/li&gt;&#10;&lt;li&gt;Devil Daggers (PC, 2016)&lt;/li&gt;&#10;&lt;li&gt;Hellblade: Senua&amp;rsquo;s Sacrifice (PC, 2017) 🌧️&lt;/li&gt;&#10;&lt;li&gt;Night in the Woods (PC, 2017) ☀️ (Probably my favourite game of 2017)&lt;/li&gt;&#10;&lt;li&gt;Pokemon Ultra Sun (3DS, 2017)&lt;/li&gt;&#10;&lt;li&gt;Super Mario Odyssey (Switch, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Flinthook (PC, 2017) 🌧️&lt;/li&gt;&#10;&lt;li&gt;Her Story (PC, 2015) ☀️&lt;/li&gt;&#10;&lt;li&gt;Dishonored 2 (PC, 2016)&lt;/li&gt;&#10;&lt;li&gt;Undertale (PC, 2015) ☀️&lt;/li&gt;&#10;&lt;li&gt;Thirty Flights of Loving (PC, 2012)&lt;/li&gt;&#10;&lt;li&gt;Everything (PC, 2017)&lt;/li&gt;&#10;&lt;li&gt;Tooth and Tail (PC, 2017)&lt;/li&gt;&#10;&lt;li&gt;Witcher 2: Assassins of Kings (PC, 2011) ☀️&lt;/li&gt;&#10;&lt;li&gt;Reigns: Her Majesty (iOS, 2017)&lt;/li&gt;&#10;&lt;li&gt;Chasing Birds (PC, 2017)&lt;/li&gt;&#10;&lt;li&gt;Dujanah (PC, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Persona 5 (PC, 2017) ☀️&lt;/li&gt;&#10;&lt;li&gt;Final Fantasy XII (PC, 2017)&lt;/li&gt;&#10;&lt;li&gt;Golf Story (Switch, 2017)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</content:encoded></item><item><title>one-fifty: Virginia (2016)</title><link>https://www.joelotter.com/posts/2017/10/virginia/</link><pubDate>Wed, 18 Oct 2017 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2017/10/virginia/</guid><description>&lt;p&gt;Virginia is one of the best films I&amp;rsquo;ve played this year.&lt;/p&gt;</description><content:encoded>&lt;p&gt;Virginia is one of the best films I&amp;rsquo;ve played this year.&lt;/p&gt;&#10;&lt;p&gt;This may sound unnecessarily sardonic, but it&amp;rsquo;s both a positive and negative critique. Terms like &amp;ldquo;shot&amp;rdquo; and &amp;ldquo;framed&amp;rdquo; are hard to use in describing video games without wincing, but the way the first person camera is used to deliver narrative beats is often really striking.&lt;/p&gt;&#10;&lt;p&gt;My favourite moment came in one scene where a character was offering me a cup of coffee on the tray. When I took it, he moved away, revealing a character behind him I hadn&amp;rsquo;t expected to see - crucially, that reveal was given as a result of one of my actions.&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s a lovely little bit of player agency that is sadly missing from the rest of Virginia, its environments not nearly as effectively used for storytelling as they could be. I&amp;rsquo;d love more games that let me be a passenger, though. Quite literally.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;&lt;em&gt;&lt;a href="https://www.joelotter.com/2017/07/31/one-fifty"&gt;one-fifty&lt;/a&gt; is a project in which I try to write about games in exactly 150 words. Something you think I should play? &lt;a href="https://twitter.com/JoelOtter"&gt;Tweet me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>one-fifty: What Remains of Edith Finch (2017)</title><link>https://www.joelotter.com/posts/2017/08/one-fifty-edith-finch/</link><pubDate>Sat, 12 Aug 2017 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2017/08/one-fifty-edith-finch/</guid><description>&lt;p&gt;In my early teens, after experiencing my first death in the family, I developed an odd fascination with the preservation of spaces.&lt;/p&gt;</description><content:encoded>&lt;p&gt;In my early teens, after experiencing my first death in the family, I developed an odd fascination with the preservation of spaces.&lt;/p&gt;&#10;&lt;p&gt;I missed my grandmother, of course, but coupled with that was the sense of a loss of &lt;em&gt;place&lt;/em&gt;. I&amp;rsquo;d never again visit that house, sit in that armchair with my feet dangling above the floor, nod off to the soothing sound of the pendulum in the wooden wall clock.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;d realised I&amp;rsquo;d been on this earth a short time, and that the places I&amp;rsquo;d assumed would be permanent were anything but. I took a lot of photographs.&lt;/p&gt;&#10;&lt;p&gt;Edith Finch tells varied, connected stories, and asks several important questions, but the one I found the most interesting was: is there value in preserving these physical snapshots of a person&amp;rsquo;s life, when that person is gone?&lt;/p&gt;&#10;&lt;p&gt;Giant Sparrow have made something utterly compelling, and I think &lt;a href="https://www.youtube.com/watch?v=W0YxjH09TDU"&gt;Tom Waits was right&lt;/a&gt;.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;&lt;em&gt;&lt;a href="https://www.joelotter.com/2017/07/31/one-fifty"&gt;one-fifty&lt;/a&gt; is a project in which I try to write about games in exactly 150 words. Something you think I should play? &lt;a href="https://twitter.com/JoelOtter"&gt;Tweet me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>one-fifty: a new writing project</title><link>https://www.joelotter.com/posts/2017/07/one-fifty/</link><pubDate>Mon, 31 Jul 2017 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2017/07/one-fifty/</guid><description>&lt;p&gt;I love games. I love playing games, I love reading about games, but I&amp;rsquo;ve realised that what I love most is &lt;em&gt;talking&lt;/em&gt; about games, as anyone who&amp;rsquo;s spent time around me can attest.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I love games. I love playing games, I love reading about games, but I&amp;rsquo;ve realised that what I love most is &lt;em&gt;talking&lt;/em&gt; about games, as anyone who&amp;rsquo;s spent time around me can attest.&lt;/p&gt;&#10;&lt;p&gt;I don&amp;rsquo;t write about games much, though, and this is because I know I&amp;rsquo;m pretty average at it.&lt;/p&gt;&#10;&lt;p&gt;The best way to improve at any craft is to &lt;em&gt;do&lt;/em&gt; it, so I&amp;rsquo;m trying to write a bit about the games that I play. By a bit I mean a &lt;em&gt;little&lt;/em&gt; bit - each of these posts will be exactly one hundred and fifty words long, the better to learn how to get my point across as concisely as I can. Like this one.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ll never be able to say everything I want to in such a small space, but maybe it&amp;rsquo;ll spur me to write something a little longer.&lt;/p&gt;&#10;&lt;p&gt;The image is from Virginia. It is good. Bye.&lt;/p&gt;</content:encoded></item><item><title>Joel's Top 5 (2016)</title><link>https://www.joelotter.com/posts/2016/12/top-5-2016/</link><pubDate>Fri, 30 Dec 2016 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2016/12/top-5-2016/</guid><description>&lt;p&gt;What a great year, eh? OK, so 2016&amp;rsquo;s been fairly appalling, but that doesn&amp;rsquo;t mean there haven&amp;rsquo;t been some great films, games, albums and a bunch of other stuff. Here are my top Things this year, from as many genres of Thing as I can be bothered.&lt;/p&gt;</description><content:encoded>&lt;p&gt;What a great year, eh? OK, so 2016&amp;rsquo;s been fairly appalling, but that doesn&amp;rsquo;t mean there haven&amp;rsquo;t been some great films, games, albums and a bunch of other stuff. Here are my top Things this year, from as many genres of Thing as I can be bothered.&lt;/p&gt;&#10;&lt;p&gt;If you, on the other hand, &lt;em&gt;can&amp;rsquo;t&lt;/em&gt; be bothered, here are some quick links to the bits you (might) care about.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="#games"&gt;Games from this year&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="#games-played"&gt;Games I played this year&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="#albums"&gt;Albums&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="#films"&gt;Films&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Peace, love and look after yourself out there.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="games"&gt;Games&lt;/h2&gt;&#10;&lt;p&gt;I&amp;rsquo;ve limited this section specifically to games I played this year, which actually came out this year. For my favourite games I played this year, there&amp;rsquo;s a separate section. Lucky you.&lt;/p&gt;&#10;&lt;h3 id="5-titanfall-2-respawn"&gt;5: Titanfall 2 (Respawn)&lt;/h3&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/12/titanfall2.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Titanfall 2 came as a complete surprise - I&amp;rsquo;d never even gone near the original, and I haven&amp;rsquo;t really taken much interest in shooters in general for a few years now.&lt;/p&gt;&#10;&lt;p&gt;Oh boy oh boy. Titanfall is &lt;em&gt;excellent&lt;/em&gt;. The single-player campaign is short, but packed full of incredibly inventive level design. It&amp;rsquo;s almost at Nintendo levels - that classic loop of introduce a new concept, iterate on it, then repeat. It&amp;rsquo;s also got a curiously touching storyline, even if some of the bosses are a bit silly.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve also been playing the multiplayer quite a bit, and have been surprised to find myself really enjoying it, despite the CoD-style levelling system. It&amp;rsquo;s sharp, it&amp;rsquo;s incredibly fast, and on a technical level it performs extremely well. I&amp;rsquo;m sad that the launch was botched - placing it right between new Battlefield and Call of Duty games feels almost like a calculated slight. There might be some weird internal publisher politics at play. Regardless, I&amp;rsquo;ll remember Titanfall 2 as a really standout FPS. Kudos, Respawn.&lt;/p&gt;&#10;&lt;center&gt;&lt;blockquote class="twitter-tweet" data-lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;I call this piece &amp;#39;The Little Sentry Who Could&amp;#39;. Titanfall 2 is the absolute bizniss&lt;a href="https://t.co/nb8GdFZ2bi"&gt;https://t.co/nb8GdFZ2bi&lt;/a&gt; &lt;a href="https://t.co/FfTEA15GcO"&gt;pic.twitter.com/FfTEA15GcO&lt;/a&gt;&lt;/p&gt;&amp;mdash; Joel Auterson (@JoelOtter) &lt;a href="https://twitter.com/JoelOtter/status/806662990342799361"&gt;December 8, 2016&lt;/a&gt;&lt;/blockquote&gt;&#10;&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;&lt;/center&gt;&#10;&lt;h3 id="4-firewatch-campo-santo"&gt;4: Firewatch (Campo Santo)&lt;/h3&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/12/firewatch.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I saw Firewatch at E3 2015, and was intruiged - then promptly forgot all about it. After some encouragement from a friend, I spent a Saturday afternoon playing through it.&lt;/p&gt;&#10;&lt;p&gt;Firewatch is, technically, a &amp;ldquo;walking simulator&amp;rdquo;, but it lifts this art form to such an extent that you can almost forget about some of the &lt;a href="https://en.wikipedia.org/wiki/Everybody's_Gone_to_the_Rapture"&gt;bullshit&lt;/a&gt; that fills that particular genre. The writing is snappy, the storyline is intruiging and the game&amp;rsquo;s audio does a tremendous job of creating a sense of vague unease, despite the gorgeous and bright Wyoming scenery. My only gripe is the framerate, which could really be better, but that&amp;rsquo;s a small complaint in the face of what is a very finely crafted game.&lt;/p&gt;&#10;&lt;h3 id="3-final-fantasy-xv-square-enix"&gt;3: Final Fantasy XV (Square Enix)&lt;/h3&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/12/ffxv.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I got this game as a Christmas present and it&amp;rsquo;s pushed Starbound off the list - sorry, Starbound.&lt;/p&gt;&#10;&lt;p&gt;Final Fantasy XV is the first FF game I&amp;rsquo;ve played in earnest since VI, so I came in almost as a newcomer to the series. To be clear: I&amp;rsquo;m not really a big JRPG player, and outside of the Final Fantasy series I haven&amp;rsquo;t played very many, so I have no idea how this holds up to some of its peers.&lt;/p&gt;&#10;&lt;p&gt;The game opens with the main character pushing a fancy car through the desert, cracking jokes with his boys, while Florence + The Machine play a cover of Stand By Me. It&amp;rsquo;s gorgeous.&lt;/p&gt;&#10;&lt;p&gt;I had to do a lot of homework to understand what the hell was going on in this game&amp;rsquo;s world, including a two hour film (dreadful) and an hour-long series of anime shorts (actually pretty watchable). Was it worth it? I&amp;rsquo;m not really sure yet - but it doesn&amp;rsquo;t matter.&lt;/p&gt;&#10;&lt;p&gt;The story makes sense &lt;em&gt;so far&lt;/em&gt;, the combat is fun and punchy, and the voice acting is generally pretty good (at least if you listen in Japanese). It&amp;rsquo;s also got all your usual Final Fantasy mechanics - potions, Phoenix Down, chocobos and some guy called Cid all make an appearance.&lt;/p&gt;&#10;&lt;p&gt;What really raises this game head and shoulders above some others I&amp;rsquo;ve played, though, is the relationship between the four main characters. Noctis is your typical Final Fantasy protagonist - slightly whiny Chosen One with great/awful hair. This would normally be a turn-off, were it not for his three pals making fun of him at any given opportunity. The four - Noctis, Ignis, Gladiolus and Prompto - chatter to each other throughout the game, and while I&amp;rsquo;ve heard a few repeated lines of dialogue, the writing never feels particularly lazy, and I&amp;rsquo;m yet to find them irritating.&lt;/p&gt;&#10;&lt;p&gt;The setting of Eos is also really immersive - by plopping players into a world which is similar to our own in terms of technology (Noctis has a smartphone, though thankfully not Candy Crush), but with the addition of magic and fantastic creatures and landscapes, exploration never gets boring.&lt;/p&gt;&#10;&lt;p&gt;There&amp;rsquo;s so much depth to this game that it&amp;rsquo;s actually quite hard to describe, and I&amp;rsquo;m not really going to try - but for giving me a road trip with my boys, complete with magic and oversized swords, Final Fantasy XV deserves a spot in this list. I feel inexplicably happy playing it, and you can&amp;rsquo;t put a numerical score on that.&lt;/p&gt;&#10;&lt;h3 id="2-uncharted-4-naughty-dog"&gt;2: Uncharted 4 (Naughty Dog)&lt;/h3&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/12/uncharted4.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I was very, very nervous about this one. Naughty Dog were coming off the back of the fantastic The Last Of Us, and making a return to Nathan Drake - the face of a franchise which, honestly, I&amp;rsquo;d always considered to be a bit overrated.&lt;/p&gt;&#10;&lt;p&gt;On one hand, the Uncharted games have always been great stories which lean far too heavily on a &lt;em&gt;very&lt;/em&gt; mediocre combat system. On the other, I was invested in the characters, and wanted them to get the ending they deserved. Did the series&amp;rsquo; finale deliver? Oh shit yes.&lt;/p&gt;&#10;&lt;p&gt;Uncharted 4 represents the globe-trotting, cinematic &lt;em&gt;adventure&lt;/em&gt; that I&amp;rsquo;d always wanted, coupled with some small gameplay changes which, when taken as a whole, remove pretty much every complaint I had about the rest of the series. Gone are the waves of identical enemies, to be mostly replaced with some simple but fun stealth sections.&lt;/p&gt;&#10;&lt;p&gt;As ever, the game is stunningly pretty, with facial animation and voice/motion capture which no other game studio ever really comes close to. As I write this I&amp;rsquo;m watching my brother play through the game, and it&amp;rsquo;s just as enjoyable when not holding the controller. The epilogue is also one of the most elegant ways I&amp;rsquo;ve seen a game series end. Please, Naughty Dog - let this be the end.&lt;/p&gt;&#10;&lt;h3 id="1-the-witness-thekla"&gt;1: The Witness (Thekla)&lt;/h3&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/12/thewitness.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve written about The Witness &lt;a href="http://felixonline.co.uk/games/5909/the-witness/"&gt;before&lt;/a&gt;, and will probably continue talking about it for years to come. On a simple level, this is one of the sharpest, most finely-crafted games in recent memory, but it&amp;rsquo;s so much more.&lt;/p&gt;&#10;&lt;p&gt;I always appreciate it when a game changes the way I see the world around me - as art should! Games are art! You wanna take this outside? - but The Witness took this to a new level. I was seeing maze puzzles &lt;em&gt;everywhere&lt;/em&gt;. Traffic lights, lines of code, the &lt;em&gt;clouds&lt;/em&gt;, just everywhere. The Witness. Read my &lt;a href="http://felixonline.co.uk/games/5909/the-witness/"&gt;review&lt;/a&gt;. Then just bloody play it. It&amp;rsquo;s sensational.&lt;/p&gt;&#10;&lt;h3 id="honourable-mention-pokemon-go-niantic"&gt;Honourable mention: Pokemon Go (Niantic)&lt;/h3&gt;&#10;&lt;p&gt;Pokemon Go gets a mention. As a game, it was, er, pretty bad. As a cultural and social phenomenon? Unparalleled in the history of gaming, maybe. 2016 has been a big dark period, but there was a two-week gap where we all lived in a sort of beautiful dream. That dream was Pokemon Go.&lt;/p&gt;&#10;&lt;p&gt;I wrote a &lt;a href="https://www.joelotter.com/2016/07/30/pokemon-go"&gt;thing about it&lt;/a&gt;, give it a read if you like.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="games-played"&gt;Games I played&lt;/h2&gt;&#10;&lt;p&gt;&lt;em&gt;Two&lt;/em&gt; sections for video games? Er, what did you expect?&lt;/p&gt;&#10;&lt;p&gt;For this section, I want to talk about the five games that most impacted me this year, despite this not being their launch year. In most cases it&amp;rsquo;s because I played them for the first time in 2016, but that&amp;rsquo;s not so for all of them. Have a read.&lt;/p&gt;&#10;&lt;h3 id="5-the-stanley-parable-davey-wreden"&gt;5: The Stanley Parable (Davey Wreden)&lt;/h3&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/12/stanley.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;My brother wanted to get me a game on Steam for my birthday, and since I&amp;rsquo;d been meaning to look at it for a long time, I asked for The Stanley Parable.&lt;/p&gt;&#10;&lt;p&gt;I spent around three hours with this game, and I think I managed to discover pretty much everything; a single playthrough takes just twenty minutes. The Stanley Parable is easily one of the funniest games in recent memory, but also has a lot of interesting points to make about the state of modern game design. The narration is absolutely spot-on, too.&lt;/p&gt;&#10;&lt;p&gt;There&amp;rsquo;s not a huge amount I can say about this one without giving away some of the surprises, but it&amp;rsquo;s pretty cheap on Steam and I &lt;em&gt;strongly&lt;/em&gt; recommend you give it a go. It even ran on my Macbook. Nice.&lt;/p&gt;&#10;&lt;h3 id="4-the-last-of-us-naughty-dog"&gt;4: The Last Of Us (Naughty Dog)&lt;/h3&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/12/tlou.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I finally got around to playing TLOU in January (thanks, little brother), and spent the majority of a few days playing through it.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m not going to say that this game is some kind of paragon of video game storytelling. To me, the greatest storytelling in video games is going to be something that &lt;em&gt;could not be done&lt;/em&gt; in any other medium - The Last Of Us, like the Uncharted series, tells its story as a collection of cutscenes and scattered dialogue pieces, and as such it&amp;rsquo;s something a little closer to traditional film-style exposition.&lt;/p&gt;&#10;&lt;p&gt;That said - the story told in The Last Of Us blew me away. Touching, weighty and lastingly grim, it left me a little bit shaken in a way that I hadn&amp;rsquo;t got from a game for some time. The main character&amp;rsquo;s called Joel, too, which helps. Here, truly, is a game that absolutely does not need a sequel. What? &lt;a href="http://www.eurogamer.net/articles/2016-12-03-the-last-of-us-2-announced-ellie-and-joel-to-return"&gt;Oh.&lt;/a&gt;&lt;/p&gt;&#10;&lt;h3 id="3-the-witcher-3-wild-hunt-cd-projekt-red"&gt;3: The Witcher 3: Wild Hunt (CD Projekt RED)&lt;/h3&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/12/witcher3.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;2016 has been a bit shit for me, especially in the latter half. In a winter where I was once again hounded by mental health bullshit, a heavy dose of escapism was just what I needed. The Witcher 3 delivers, and then some.&lt;/p&gt;&#10;&lt;p&gt;This game will henceforth be what I look to as the gold standard for an open world RPG - the combat is interesting, the writing is superb, the voice acting is so &lt;em&gt;comprehensive&lt;/em&gt; and, of course, the world itself is beautifully crafted. I&amp;rsquo;ve sunk about 30 hours into Geralt&amp;rsquo;s latest adventure and I am nowhere near the end of the main game, let alone the (apparently fantastic) DLC.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve noticed, through my own observations and conversations with friends, that when I&amp;rsquo;m not feeling great the games I turn to - The Witcher, Bloodborne, The Last Of Us - are universally pretty grim settings. Doesn&amp;rsquo;t this make me feel &lt;em&gt;worse&lt;/em&gt;?&lt;/p&gt;&#10;&lt;p&gt;There&amp;rsquo;s a whole article to be written about this, and I&amp;rsquo;ll get around to it at some point, but I think the central point is this: Geralt is far better equipped to deal with his world than I am with mine. It&amp;rsquo;s a power fantasy? Maybe. Is that inherently a bad thing? We&amp;rsquo;ll talk.&lt;/p&gt;&#10;&lt;h3 id="2-dark-souls-fromsoftware"&gt;2: Dark Souls (FromSoftware)&lt;/h3&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/12/darksouls.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;d played Dark Souls before, but this year was the one where I really committed to it. Over the course of the month of April, I alternated between working on my &lt;a href="http://www.joelotter.com/kajero"&gt;Master&amp;rsquo;s project&lt;/a&gt; and slowly uncovering Lordran&amp;rsquo;s many secrets.&lt;/p&gt;&#10;&lt;p&gt;There have been many, &lt;em&gt;many&lt;/em&gt; &lt;a href="http://www.gamesradar.com/why-dark-souls-friendliest-most-benevolent-game-its-generation/"&gt;pieces&lt;/a&gt; &lt;a href="http://www.gamesradar.com/why-dark-souls-simply-greatest-game-all-time/"&gt;written&lt;/a&gt; &lt;a href="http://you-died.com/book/"&gt;about&lt;/a&gt; the elegance of Dark Souls&amp;rsquo; overlapping systems, the genius of its core gameplay loop and its unique approach to delivering storytelling in games. They&amp;rsquo;re pretty much all true.&lt;/p&gt;&#10;&lt;p&gt;Most of the time when I play games, I enjoy myself but feel like I&amp;rsquo;m not actually accomplishing anything - it&amp;rsquo;s just leisure time, right? Not so with Dark Souls - in overcoming this game&amp;rsquo;s challenges, I learned a lot about myself and how my mind operates. It also meant that on the days when I ran into a brick wall somewhere in the JavaScript ecosystem, at least I could beat another boss and feel like I&amp;rsquo;d achieved something real that day. The moment when I finally took down Ornstein and Smough is very clear in my memory as one of the defining points of my gaming career.&lt;/p&gt;&#10;&lt;p&gt;The meme that&amp;rsquo;s grown up around Dark Souls is the chatter about how hard it is. It&amp;rsquo;s certainly not an easy game, but there&amp;rsquo;s so much more to it than that.&lt;/p&gt;&#10;&lt;p&gt;Dark Souls may just be the finest video game ever created, and it&amp;rsquo;s likely FromSoftware&amp;rsquo;s crowning achievement. It&amp;rsquo;s not my &lt;em&gt;favourite&lt;/em&gt; From game, though.&lt;/p&gt;&#10;&lt;h3 id="1-bloodborne-fromsoftware"&gt;1: Bloodborne (FromSoftware)&lt;/h3&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/12/bloodborne.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Bloodborne. Oh, Bloodborne.&lt;/p&gt;&#10;&lt;p&gt;There&amp;rsquo;s a real temptation to compare Bloodborne to the Dark Souls games. I have a lot of friends who&amp;rsquo;ve done just that, and found it wanting.&lt;/p&gt;&#10;&lt;p&gt;Fundamentally, though, they are not the same. While Dark Souls&amp;rsquo; lore is sprawling and esoteric, Bloodborne&amp;rsquo;s is incredibly focused, if no less difficult to piece together. When I play Dark Souls, I play it as a very patient, rhythmical game. Bloodborne, on the other hand, is an elegant, terrifying dervish. It&amp;rsquo;s ballet with a chainsaw. It&amp;rsquo;s utterly glorious, and revels in its own fundamental ridiculousness.&lt;/p&gt;&#10;&lt;p&gt;Bloodborne was my game of 2015, but by the end of the year I still hadn&amp;rsquo;t finished it. The first couple of months of 2016 turned into a sort of holding pattern. I&amp;rsquo;d go to uni during the day (this includes weekends&amp;hellip;Imperial). When I got home, I&amp;rsquo;d carry on working on coursework - up until 11pm. The hours between 11pm and 1am were sacred.&lt;/p&gt;&#10;&lt;p&gt;During these two hours, I was a hunter of the dream. I was unravelling the story of how Yarnham came to be the way it is, and hunting ever bigger and badder beasts, until the sky unfolded itself and I grew eyes on the inside.&lt;/p&gt;&#10;&lt;p&gt;&amp;hellip;I got pretty into it, is what I&amp;rsquo;m saying. The fact is, this game became a really important part of my life for a little while. I was pretty unhappy last winter for no apparent reason, and this game provided an escape.&lt;/p&gt;&#10;&lt;p&gt;Bloodborne isn&amp;rsquo;t my game of the year, because it didn&amp;rsquo;t come out this year. It might just be my game of the decade, though.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="albums"&gt;Albums&lt;/h2&gt;&#10;&lt;p&gt;My favourite genre of music is and probably always will be hip hop. This year saw a large number of big-name hip hop releases - we had new Drake, new Kanye, new Kano, new(ish) Skepta, new J Cole and loads more.&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s odd, then, that few of these really grabbed me enough to land a top five spot. Here are my favourite albums from this year.&lt;/p&gt;&#10;&lt;h3 id="5-cashmere-swet-shop-boys"&gt;5: Cashmere (Swet Shop Boys)&lt;/h3&gt;&#10;&lt;iframe src="https://embed.spotify.com/?uri=spotify%3Aalbum%3A0wL2jTDIlsPrvwEm7Le0ML" width="100%" height="380" frameborder="0" allowtransparency="true"&gt;&lt;/iframe&gt;&#10;&lt;p&gt;I found this album by accident and I&amp;rsquo;m very glad I did. Swet Shop Boys are a team-up of Heems (of Das Racist fame) and Riz MC, AKA Riz Ahmed, AKA Britain&amp;rsquo;s answer to Donald Glover? Maybe?&lt;/p&gt;&#10;&lt;p&gt;As you&amp;rsquo;d expect of an album Heems is involved in, Cashmere is hilarious, but it&amp;rsquo;s more than that. Here is an open and frank discussion of what it&amp;rsquo;s like being a person of colour in a post-9/11 society, and how that impacts in places from personal lives to the music industry.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Standout track&lt;/strong&gt;: Zayn Malik, because Heems&amp;rsquo; verse:&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;I am a radiator, I am a toaster&lt;/em&gt;&lt;br&gt;&#10;&lt;em&gt;I am a college dorm room poster&lt;/em&gt;&lt;br&gt;&#10;&lt;em&gt;I am an idiot, I&amp;rsquo;m good at rapping,&lt;/em&gt;&lt;br&gt;&#10;&lt;em&gt;Get on the beat, murk it, then ask what happened&lt;/em&gt;&lt;/p&gt;&#10;&lt;h3 id="4-the-impossible-kid-aesop-rock"&gt;4: The Impossible Kid (Aesop Rock)&lt;/h3&gt;&#10;&lt;iframe src="https://embed.spotify.com/?uri=spotify%3Aalbum%3A1An1m0S3ZdQy9Uuo476D12" width="100%" height="380" frameborder="0" allowtransparency="true"&gt;&lt;/iframe&gt;&#10;&lt;p&gt;I love Aesop Rock, so it&amp;rsquo;s no surprise this album ended up on the list given I&amp;rsquo;ve been waiting for it for four years. What surprised me was just how good The Impossible Kid is.&lt;/p&gt;&#10;&lt;p&gt;Skelethon was a brutally honest look at the way Aes sees himself. This album is more of the same, but rather than a faintly heartbreaking essay on his lowest moments, this presents a more upbeat, optimistic outlook.&lt;/p&gt;&#10;&lt;p&gt;The prevailing theme of The Impossible Kid is growing older. Tracks like Rings deal with Aesop&amp;rsquo;s regrets at not fulfilling his potential (&lt;em&gt;I used to paint / hard to admit that I &lt;strong&gt;used&lt;/strong&gt; to paint&lt;/em&gt;), while Lotta Years is a really funny &amp;ldquo;these damn kids&amp;rdquo; with, as ever, a heart of gold.&lt;/p&gt;&#10;&lt;p&gt;This album came out in April and it&amp;rsquo;s still on my rotation. Highly recommended.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Standout track&lt;/strong&gt;: Shrunk&lt;/p&gt;&#10;&lt;h3 id="3-the-wild-swan-foy-vance"&gt;3: The Wild Swan (Foy Vance)&lt;/h3&gt;&#10;&lt;iframe src="https://embed.spotify.com/?uri=spotify%3Aalbum%3A7f0mA4UoOWEZ6UQ8Aa4ieD" width="100%" height="380" frameborder="0" allowtransparency="true"&gt;&lt;/iframe&gt;&#10;&lt;p&gt;Foy Vance released Joy of Nothing in 2013, after six years without an album. It was a great comeback, and combined with a tour with Ed Sheeran, helped promote him to the kind of global attention that he so richly deserves - but I wasn&amp;rsquo;t hugely taken with it, especially given how much I adored Hope.&lt;/p&gt;&#10;&lt;p&gt;While the first album was characterised by an endearing, almost wide-eyed optimism, Joy of Nothing left a bitter taste. The Wild Swan, while not a return to the tone of Hope, seems to represent a much more settled Foy. Or maybe I&amp;rsquo;m reading far too much into it because I love the man so much. Who knows.&lt;/p&gt;&#10;&lt;p&gt;I got to see this album performed in its entirety at Shepherd&amp;rsquo;s Bush in November and it was a really special one.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Standout track&lt;/strong&gt;: Bangor Town. It&amp;rsquo;s just gorgeous.&lt;/p&gt;&#10;&lt;h3 id="2-22-a-million-bon-iver"&gt;2: 22, A Million (Bon Iver)&lt;/h3&gt;&#10;&lt;iframe src="https://embed.spotify.com/?uri=spotify%3Aalbum%3A1PgfRdl3lPyACfUGH4pquG" width="100%" height="380" frameborder="0" allowtransparency="true"&gt;&lt;/iframe&gt;&#10;&lt;p&gt;When Vernon&amp;rsquo;s first album as Bon Iver was released, it turned the music industry on its head, and the number of jumper-wearing, falsetto-forcing folk singers multiplied exponentially. &lt;em&gt;For Emma, Forever Ago&lt;/em&gt; was a gorgeous album, and its successor, the self-titled &lt;em&gt;Bon Iver&lt;/em&gt;, continued in the same vein, albeit with much less that felt new or innovative.&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;22, A Million&lt;/em&gt;, then, represents another shift in style, and I think the influences of Vernon&amp;rsquo;s time spent working with the likes of Kanye are much more apparent. This album is extraordinarily inventive, and like nothing else I&amp;rsquo;ve ever heard.&lt;/p&gt;&#10;&lt;p&gt;I spent the whole week after this album launched listening to it pretty much on a loop. This was a week during which I knew I was about to get dumped. There&amp;rsquo;s a track on it called &lt;strong&gt;22 (OVER S∞∞N)&lt;/strong&gt;. You work it out.&lt;/p&gt;&#10;&lt;p&gt;In actual fact, that&amp;rsquo;s not what this track is about at all, and indeed Vernon has been pretty open about the overarching theme of this record - his career, fame and its consequences, et cetera.&lt;br&gt;&#10;I&amp;rsquo;ve always thought that the mark of a great piece of art is that the viewer/listener/player can attach their own meaning to it without the original meaning being lost. With that in mind, 22, A Million is a breakup album, and nobody is going to tell me otherwise.&lt;/p&gt;&#10;&lt;p&gt;In all seriousness - I believe this album is the start of another shift for the folk genre, and we&amp;rsquo;ll see a lot of imitation soon enough. For that, &lt;em&gt;22, A Million&lt;/em&gt; is my album of the year.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Standout track&lt;/strong&gt;: 715 - CR∑∑KS&lt;/p&gt;&#10;&lt;p&gt;&amp;hellip;hang on.&lt;/p&gt;&#10;&lt;p&gt;What? They released &lt;em&gt;what&lt;/em&gt;?&lt;/p&gt;&#10;&lt;h3 id="1-run-the-jewels-3-run-the-jewels"&gt;1: Run The Jewels 3 (Run The Jewels)&lt;/h3&gt;&#10;&lt;iframe src="https://embed.spotify.com/?uri=spotify%3Aalbum%3A7q1uNF3HT1wTjxuMW0rFJn" width="100%" height="380" frameborder="0" allowtransparency="true"&gt;&lt;/iframe&gt;&#10;&lt;p&gt;This album ruined my list! I had to go and rewrite my whole list! Thank you &lt;em&gt;so&lt;/em&gt; much, Run The Jewels.&lt;/p&gt;&#10;&lt;p&gt;Scheduled for release in the first couple of weeks of January, RTJ3 was dropped as a surprise release on Christmas Eve, and it was possibly the best Christmas present I could have received, as well as a half-decent ending to this awful year.&lt;/p&gt;&#10;&lt;p&gt;Run The Jewels 2 was a very focused record, coming in at less than 40 minutes in length. The sequel is longer, but somehow manages not to feel bloated.&lt;/p&gt;&#10;&lt;p&gt;El-P&amp;rsquo;s production has somehow managed to improve &lt;em&gt;again&lt;/em&gt;, meshing RTJ2&amp;rsquo;s heavy beats with a subtle layering that puts me in mind of Cancer 4 Cure, El&amp;rsquo;s last solo record. The vocals are, as ever, outstanding, with Killer Mike flowing effortlessly over the top of rhythms that would give almost any other rapper pause for thought.&lt;/p&gt;&#10;&lt;p&gt;The Run The Jewels style has always been &amp;ldquo;fun, but with serious undertones&amp;rdquo;, and the new album continues this. Tracks like Panther Like A Panther are over the top, bombastic and faintly ridiculous, while Thursday In The Danger room relates two stories about the deaths of friends.&lt;/p&gt;&#10;&lt;p&gt;The album is relatively light on features, though longtime collaborators Boots and Zach de la Rocha make appearances. There&amp;rsquo;s also some stunning saxophone from Kamasi Washington, perhaps best known for his work on Kendrick&amp;rsquo;s &lt;em&gt;To Pimp A Butterfly&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;I still haven&amp;rsquo;t decided if this is my favourite Run The Jewels record, but it&amp;rsquo;s certainly my favourite record of 2016. Thanks, boys.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Standout track&lt;/strong&gt;: Talk To Me&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="films"&gt;Films&lt;/h2&gt;&#10;&lt;p&gt;Honestly, I thought this was a pretty thin year for cinema - with the provisio, of course, that I&amp;rsquo;m hardly a film buff. Anyway, here are my top five for this year.&lt;/p&gt;&#10;&lt;h3 id="5-the-boy-and-the-beast"&gt;5: The Boy and the Beast&lt;/h3&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/12/boyandbeast.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;This is technically a 2015 film, but as its western release was in 2016 I&amp;rsquo;m going to cheat and put it on here.&lt;/p&gt;&#10;&lt;p&gt;The Boy and the Beast is about a young boy who stumbles into another world, and spends years there being trained in martial arts by a grumpy bear-thing. It&amp;rsquo;s exactly as daft as it sounds, but yet manages to be a really touching story about growing up, parenthood and being a little bit different.&lt;/p&gt;&#10;&lt;p&gt;The ending is, of course, complete nonsense, but it all somehow holds together, and stuck with me a lot longer than I thought it would have. That&amp;rsquo;s why it&amp;rsquo;s on this list.&lt;/p&gt;&#10;&lt;h3 id="4-rogue-one-a-star-wars-story"&gt;4: Rogue One: A Star Wars Story&lt;/h3&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/12/rogueone.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I went through quite a long phase of not liking Star Wars because of how hammed-up and silly it was. Now I adore it for exactly those reasons.&lt;/p&gt;&#10;&lt;p&gt;Rogue One is a very good film. I really enjoyed it.&lt;/p&gt;&#10;&lt;p&gt;&amp;hellip;and yet I can&amp;rsquo;t find much else to say about it. It &lt;em&gt;feels&lt;/em&gt; like a Star Wars film, and yet somehow it isn&amp;rsquo;t really one. The Force Awakens was essentially just a reboot of A New Hope, and yet I went to see it twice; I&amp;rsquo;m not in any great rush to see this one again, and I can&amp;rsquo;t really explain why.&lt;/p&gt;&#10;&lt;h3 id="3-zootopia"&gt;3: Zootopia&lt;/h3&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/12/zootopia.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I point-blank refuse to refer to this film as Zootropolis. Go away.&lt;/p&gt;&#10;&lt;p&gt;Zootopia is one of the best Pixar flicks in years, despite not actually being made by Pixar. I loved it.&lt;/p&gt;&#10;&lt;h3 id="2-your-name-kimi-no-na-wa"&gt;2: Your Name. (Kimi no Na wa)&lt;/h3&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/12/yourname.png" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;While not the sort of film I ever really thought I&amp;rsquo;d enjoy, Your Name turned out to be stupendously good. This is another film that&amp;rsquo;s very, very hard to describe, mainly because &lt;em&gt;so much&lt;/em&gt; happens in it and there&amp;rsquo;s so much to talk about.&lt;/p&gt;&#10;&lt;p&gt;I went to see it with two friends, and when the film ended Eddie said &amp;ldquo;well, that film had at least two more explosions than I thought it would&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;Go and see it, even if only for the animation, which is excellent.&lt;/p&gt;&#10;&lt;h3 id="1-arrival"&gt;1: Arrival&lt;/h3&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/12/arrival.png" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;I heard tell that a few people were annoyed at Arrival for being &amp;ldquo;boring&amp;rdquo;. These are exactly the sort of people who deserve to be annoyed.&lt;/p&gt;&#10;&lt;p&gt;Arrival is probably the best representation of what science fiction can &lt;em&gt;be&lt;/em&gt; that I&amp;rsquo;ve seen in cinemas for a very long time. It&amp;rsquo;s smart, it&amp;rsquo;s a little bit scary, it tells a fundamentally very human story and it leaves you thinking - and a little bit numb.&lt;/p&gt;&#10;&lt;p&gt;Oh and the actors are, like, really good and stuff.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;This is the end of my list of lists. Play more games, listen to more music, watch more films, and don&amp;rsquo;t take no shit. Have a great 2017.&lt;/p&gt;</content:encoded></item><item><title>Update from the Fringe</title><link>https://www.joelotter.com/posts/2016/08/fringe/</link><pubDate>Fri, 26 Aug 2016 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2016/08/fringe/</guid><description>&lt;p&gt;My flatmate/co-host Jake bangs on my door shortly after I wake up.&lt;/p&gt;&#10;&lt;p&gt;&amp;ldquo;Coffee?&amp;rdquo;&#10;&amp;ldquo;Please mate.&amp;rdquo;&lt;/p&gt;</description><content:encoded>&lt;p&gt;My flatmate/co-host Jake bangs on my door shortly after I wake up.&lt;/p&gt;&#10;&lt;p&gt;&amp;ldquo;Coffee?&amp;rdquo;&#10;&amp;ldquo;Please mate.&amp;rdquo;&lt;/p&gt;&#10;&lt;p&gt;A few minutes later, a mug of coffee is passed around the door. It&amp;rsquo;s got milk in it. Jake always puts milk in my coffee, and I haven&amp;rsquo;t the heart to tell him I don&amp;rsquo;t take milk in coffee.&lt;/p&gt;&#10;&lt;p&gt;My mum used to put milk in coffee when she made it for me in that frantic twenty minutes between rugby training and tennis on a Saturday morning. The smell will always remind me of a big cold sky, grass stains, dirt under my fingernails, and trying to fit in. It also reminds me of having a burnt tongue, because I had no idea how to drink coffee when I was twelve.&lt;/p&gt;&#10;&lt;p&gt;The taste is strangely comforting.&lt;/p&gt;&#10;&lt;p&gt;I found a diary in a Word document on my computer recently. It has three entries, made up of a weekend and Monday during my first year of secondary school. That&amp;rsquo;s approximately the length of time I was able to commit to anything at that age. It&amp;rsquo;s a weird slice of a life I&amp;rsquo;ve almost entirely forgotten - pretending to like rugby, struggling to make friends, and attempting to stay out of trouble with scary teachers. I&amp;rsquo;m not going to share it with you.&lt;/p&gt;&#10;&lt;p&gt;I can very clearly remember my first morning of starting &amp;lsquo;big school&amp;rsquo;. I met my friend Andrew outside the school gates. I&amp;rsquo;m not exactly sure how me and Andrew were mates. He was the fastest runner, the best at football and completely confident in himself. In contrast, I wasn&amp;rsquo;t great at sports, was simultaneously chubby and scrawny, and used Oxford commas. Andrew gave me some pointers - my tie was wrong, I&amp;rsquo;d buttoned the wrong button on my blazer, my tie was still wrong, give it &lt;em&gt;here&lt;/em&gt;, right, let&amp;rsquo;s go.&lt;/p&gt;&#10;&lt;p&gt;By the end of school I was the lanky kid who somehow had managed to make friends. I still don&amp;rsquo;t know how to do a tie properly, but I still talk to those friends sometimes.&lt;/p&gt;&#10;&lt;p&gt;The Fringe is simultaneously very lonely and impossible to find time alone at. I&amp;rsquo;m pretty much constantly surrounded by artists doing incredible things. In contrast, I&amp;rsquo;m here more to wear my hat that says &amp;lsquo;promoter&amp;rsquo; on it, and sometimes I don&amp;rsquo;t necessarily feel that I&amp;rsquo;ve &lt;em&gt;created&lt;/em&gt; something. This is fine! Sometimes, though, I feel like my tie isn&amp;rsquo;t on right and my blazer doesn&amp;rsquo;t fit.&lt;/p&gt;&#10;&lt;p&gt;(That&amp;rsquo;s a metaphor because I am a &lt;strong&gt;writer&lt;/strong&gt;. We don&amp;rsquo;t actually have ties and blazers. I did see a whole horde of posh school kids the other day, though. None of them took flyers.)&lt;/p&gt;&#10;&lt;p&gt;Everyone&amp;rsquo;s in it together. Everyone seems to feel shit approximately 20% of the time. If we all synced up that&amp;rsquo;d be the end of it, but we&amp;rsquo;re carrying on somehow. There are two shows left. I think we&amp;rsquo;ll make it.&lt;/p&gt;&#10;&lt;p&gt;I went for a run up Arthur&amp;rsquo;s Seat yesterday. I sat down for a bit and looked out over Edinburgh. When I got up I had dirt under my fingernails, and caught the scent of grass stains. Jake made me coffee when I got back. It still had milk in. I didn&amp;rsquo;t say anything.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Oh, right, and our show is on every day at 18:45 in Pilgrim. It&amp;rsquo;s called &lt;a href="https://tickets.edfringe.com/whats-on/boomerang-club"&gt;Boomerang Club&lt;/a&gt; and it is free. See you there!&lt;/strong&gt;&lt;/p&gt;</content:encoded></item><item><title>Pokémon Go</title><link>https://www.joelotter.com/posts/2016/07/pokemon-go/</link><pubDate>Sat, 30 Jul 2016 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2016/07/pokemon-go/</guid><description>&lt;p&gt;This isn&amp;rsquo;t really going to be a game review. Taken purely as a game, and ignoring its wider impact, Pokémon Go is not very good. It crashed frequently before a recent update, there&amp;rsquo;s nothing to do outside populated areas, the drain on battery and mobile data is &lt;em&gt;high&lt;/em&gt;, and it&amp;rsquo;s incredibly hard for new players coming in to the game a few weeks late to catch up.&lt;/p&gt;</description><content:encoded>&lt;p&gt;This isn&amp;rsquo;t really going to be a game review. Taken purely as a game, and ignoring its wider impact, Pokémon Go is not very good. It crashed frequently before a recent update, there&amp;rsquo;s nothing to do outside populated areas, the drain on battery and mobile data is &lt;em&gt;high&lt;/em&gt;, and it&amp;rsquo;s incredibly hard for new players coming in to the game a few weeks late to catch up.&lt;/p&gt;&#10;&lt;p&gt;Let&amp;rsquo;s not even mention that there&amp;rsquo;s no actual end goal - owing to regional differences and some monsters that aren&amp;rsquo;t available at all, it&amp;rsquo;s not currently possible to catch &amp;rsquo;em all.&lt;/p&gt;&#10;&lt;p&gt;On a walk around a busy city centre in the days and weeks after release, you&amp;rsquo;d be hard pressed to remember all of this.&lt;/p&gt;&#10;&lt;p&gt;My all-time favourite sport, people-watching, has become vastly more enjoyable thanks to Pokémon Go. Here&amp;rsquo;s how a standard interaction plays out.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Person A and Person B approach one another on a busy street, country path, wherever.&lt;/li&gt;&#10;&lt;li&gt;They each notice that the other is looking at their phone.&lt;/li&gt;&#10;&lt;li&gt;Sheepish grins.&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;Are you&amp;hellip;?&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;&amp;hellip;yeah.&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;Friendship.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;It&amp;rsquo;s hilarious, it&amp;rsquo;s gorgeous, and this is happening all over the world, &lt;em&gt;all the time&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p&gt;The best kinds of game, for me, are the ones that create stories that last long after they&amp;rsquo;ve been switched off. My first of these was, of course, the original Pokémon, but there have been lots of others, across a huge variety of genres. When we first got our Xbox 360, I spent every morning in registration class swapping stories with Adam and Daniel about our exploits in Oblivion the night before. A few years later, lunchtime conversations were dominated by hilarious (er, to us) retellings of games of Halo, then Minecraft, then Borderlands and many more.&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://www.joelotter.com/img/2016/07/ffff.jpg"&gt;&lt;img src="https://www.joelotter.com/img/2016/07/ffff.jpg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;&#10;&lt;p class="caption"&gt;From left to right: me, Zach, Ridwan, Falls. Falls' Fantabulous Flying Four. Circa 2010. I am completely unabashed by how much I cherish this photo.&lt;/p&gt;&#10;&lt;p&gt;Pokémon Go is another one of these fantastic games - not even games, &lt;em&gt;experiences&lt;/em&gt;. Here are some stories from my first couple of weeks.&lt;/p&gt;&#10;&lt;h3 id="welcome-to-the-world-of-pokémon-my-name-is-oak"&gt;Welcome to the world of Pokémon! My name is Oak&amp;hellip;&lt;/h3&gt;&#10;&lt;p&gt;I installed the game when it first came out in the States, via a hopefully-not-dodgy APK sideload. I managed to convince my brother to do the same, but most of my pals around home wanted to wait until the game was actually released in the UK.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;d expected there not to be much activity and thought I could get in there early by capturing a few gyms. Boy oh boy was I wrong. The game wasn&amp;rsquo;t even out and every gym in Jordanstown was already captured.&lt;/p&gt;&#10;&lt;p&gt;On our first Poke-walk around the relatively sleepy little town we grew up in, while walking through the university grounds, Luke and me walked past a lady who looked to be in her forties. She had her phone in her hand, but we didn&amp;rsquo;t really think anything of it; we&amp;rsquo;d assumed this game would be mostly confined to those in their mid-twenties, who were children when the Pokémon craze (I suppose we should now call it the &lt;em&gt;first&lt;/em&gt; Pokémon craze?) was at its peak.&lt;br&gt;&#10;&amp;ldquo;You hunting too?&amp;rdquo;, she said, smirking.&lt;br&gt;&#10;We were too shocked to say anything. It was only a few minutes later that I said, &amp;ldquo;we should have told her about the Hypno in that car park&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;Later, at Loughshore Park, we found two Pokéstops less than a hundred metres apart. Someone had set a lure on the bandstand, and we spent a little while there catching Magnemites. As we walked on, I glanced into some of the parked cars - nearly every single person in them was playing Pokémon Go.&lt;br&gt;&#10;Interestingly, the bandstand&amp;rsquo;s description on the Pokéstop says &lt;em&gt;&amp;ldquo;dedicated to Ross Aiken&amp;rdquo;&lt;/em&gt;. I don&amp;rsquo;t know who Ross is or was, but I wonder if he&amp;rsquo;d be happy that half of Jordanstown now probably knows his name.&lt;/p&gt;&#10;&lt;p&gt;A little further on, I battled, and defeated, my first Pokémon gym (go team red yeah woo Valor). While I was in the middle of battling, a gang of four chaps around our age approached, all looking at their phones. We had a nice chat until Conor - who I recognised as someone I played badminton with years ago - said, &amp;ldquo;thanks for the gym&amp;rdquo;.&lt;br&gt;&#10;I looked down. The cheeky bastard had waited until I&amp;rsquo;d beaten the gym and then, while I tried to choose which Pokémon to leave defending it, snuck his own in under my nose. I tried to battle it again, but the rest of his mates piled all their high-level monsters in on top of it. Aaaargh.&lt;/p&gt;&#10;&lt;p&gt;A little later, after Luke had gone home, I decided to sneak back to the gym and capture it (again). I left it defended by a very high-level Hypno - the one from the car park - called &amp;lsquo;Ha Ha Conor&amp;rsquo;. Unfortunately it turns out other players can&amp;rsquo;t see your nicknames, which is understandable but a bit saddening.&lt;/p&gt;&#10;&lt;p&gt;As I headed back for home, I crossed paths with a dad and his ten-year-old, unmistakeably playing Pokémon. I was immediately accosted by a Pokémon trainer half my height.&lt;/p&gt;&#10;&lt;p&gt;&amp;ldquo;What team are you?&amp;rdquo;&lt;br&gt;&#10;&amp;ldquo;Red team.&amp;rdquo;&lt;br&gt;&#10;&amp;ldquo;I&amp;rsquo;m Yellow. Did you capture that gym?&amp;rdquo;&lt;br&gt;&#10;&amp;ldquo;Yep, just now.&amp;rdquo;&lt;br&gt;&#10;&amp;ldquo;I&amp;rsquo;m taking it from you.&amp;rdquo;&lt;br&gt;&#10;&amp;ldquo;Good luck.&amp;rdquo;&lt;/p&gt;&#10;&lt;p&gt;He sped off for the statue where the gym is, his dad chasing after him, roaring with laughter.&lt;/p&gt;&#10;&lt;p&gt;All this happened in one hour-or-two walk around my home town. None of these conversations would have happened without Pokémon Go, and the game wasn&amp;rsquo;t even out yet.&lt;/p&gt;&#10;&lt;h3 id="are-you-a-boy-or-a-girl"&gt;Are you a boy? Or a girl?&lt;/h3&gt;&#10;&lt;p&gt;I didn&amp;rsquo;t start playing in earnest until the game was properly out in the UK, and Neil, Daniel and the Ryans were able to join in. The photo at the top of this post is from one of our first proper Pokéwalks together. We got very excited about Caterpie. We were so new to this game.&lt;/p&gt;&#10;&lt;p&gt;At the gym at the entrance to the university, we met two kids, unsuccessfully trying to capture the gym. They had a chat to us about Pokémon; we were shocked to find out that they didn&amp;rsquo;t actually know what Pokémon &lt;em&gt;was&lt;/em&gt;, they just wanted to join in with the game. I&amp;rsquo;d never felt so old. We gave them a few pointers and we parted ways. I like to think they&amp;rsquo;ll dig out an old Game Boy some day and experience their first &lt;em&gt;real&lt;/em&gt; Pokémon game - I kind of envy that they still get to have that first playthrough.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/07/oak.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p class="caption" style="width: inherit;"&gt;Remember the first time? I picked Charmander. I still do.&lt;/p&gt;&#10;&lt;p&gt;Later, down by the Lough, we met a middle-aged couple sitting by the Pokéstop. We had a chat to them - the lady said she didn&amp;rsquo;t know much about the game, but they were joining Yellow team because their niece was on Yellow team.&lt;br&gt;&#10;&amp;ldquo;As long as it&amp;rsquo;s not Blue&amp;rdquo;, we said, and we all had a laugh. Except Neil, who joined Blue team, the filthy traitor.&lt;br&gt;&#10;We went on our way - a short conversation, but yet another of so many that would never have happened without this game.&lt;/p&gt;&#10;&lt;p&gt;After the others had gone home, Neil and me decided to walk back past Whiteabbey church, where there was a Pokéstop. On the way, a wild Pinsir jumped out at us - a pretty rare find. I ran out of Pokeballs half way through capturing it, so had to run up to the church, spin the Pokéstop, and run back before the Pinsir disappeared. We both managed to get it, and probably looked pretty strange to passers-by. Who cares? I GOT A PINSIR.&lt;/p&gt;&#10;&lt;p&gt;On another day, one so hot that the screen on my phone was uncomfortable to touch, we were back at that same gym outside the university. We&amp;rsquo;d managed to capture it between us a little earlier, but had seen that someone was battling it - so back we went on the defensive. A chap around our age, from Yellow team, was busily trying to take down our gym, with little success. I decided to be diplomatic.&lt;/p&gt;&#10;&lt;p&gt;&amp;ldquo;Is there any chance you could just, y&amp;rsquo;know, stop that?&amp;rdquo;&lt;br&gt;&#10;&amp;ldquo;Nope.&amp;rdquo;&lt;/p&gt;&#10;&lt;p&gt;After the standoff had gone on for a few minutes, we cunningly decided to let him whittle down the gym, and then pinch it from under his nose right before he could put a replacement Pokémon in, a trick I&amp;rsquo;d learned not so long before. We all had a good laugh about it and Jack - our new pal - came with us on our walk. We still check to see if &lt;strong&gt;JackFFX&lt;/strong&gt; is holding any gyms around Jordanstown - he&amp;rsquo;s been a bit quiet of late.&lt;/p&gt;&#10;&lt;h3 id="your-very-own-pokémon-legend-is-about-to-unfold"&gt;Your very own Pokémon legend is about to unfold!&lt;/h3&gt;&#10;&lt;p&gt;This is just a tiny selection of the things that, without Pokémon Go, would not have happened in the last few weeks. I&amp;rsquo;m still going for Pokéwalks (a real word, I promise) with friends nearly every day, and I still see hordes of people hanging around landmarks looking at their phones. Sometimes I talk to them. Sometimes I make friends.&lt;/p&gt;&#10;&lt;p&gt;There&amp;rsquo;s a fair amount of horrible shite going on in the UK and the world at present, and a lot of this summer has been characterised by political turmoil and disaster. In a few years, though, I&amp;rsquo;m willing to bet I&amp;rsquo;ll look back on this summer as the one I spent playing Pokémon Go with some of my oldest friends.&lt;/p&gt;&#10;&lt;p&gt;I don&amp;rsquo;t know if the game will have any lasting appeal beyond this summer, or whether any changes or additions Niantic make (I want a Totodile!) will keep me interested. For now, I&amp;rsquo;m going to keep going for walks with my Pokémon friends. Even the ones that don&amp;rsquo;t, y&amp;rsquo;know, actually exist.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/07/pidgey.jpg" alt=""&gt;&lt;/p&gt;</content:encoded></item><item><title>Termloop: pixel mode</title><link>https://www.joelotter.com/posts/2016/07/termloop-pixel-mode/</link><pubDate>Thu, 28 Jul 2016 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2016/07/termloop-pixel-mode/</guid><description>&lt;p&gt;I&amp;rsquo;ve been reading a lot about &lt;a href="http://www.lexaloffle.com/pico-8.php"&gt;PICO-8&lt;/a&gt; recently, and found it really interesting. For the uninitiated, PICO-8 is a &amp;lsquo;fantasy console&amp;rsquo; - a games console which exists purely as a program and set of development tools, rather than a real piece of hardware. Developers can create games for PICO-8 which are small enough to be embedded directly into a .png image, which is really cool.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I&amp;rsquo;ve been reading a lot about &lt;a href="http://www.lexaloffle.com/pico-8.php"&gt;PICO-8&lt;/a&gt; recently, and found it really interesting. For the uninitiated, PICO-8 is a &amp;lsquo;fantasy console&amp;rsquo; - a games console which exists purely as a program and set of development tools, rather than a real piece of hardware. Developers can create games for PICO-8 which are small enough to be embedded directly into a .png image, which is really cool.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;d been under the impression PICO-8 was actually a specification for a fantasy machine that anyone could build their own implementation of. Unfortunately this isn&amp;rsquo;t the case - it&amp;rsquo;s a closed-source program, which seems a bit odd considering the games have to be fully open-source and editable. Oh well. It did lead me to wonder if it&amp;rsquo;d be possible to get PICO-8 games playing in a terminal. That&amp;rsquo;s a whole &amp;rsquo;nother project, but I decided to tweak &lt;a href="https://github.com/JoelOtter/termloop"&gt;Termloop&lt;/a&gt;, my game engine, slightly, to make these sorts of games look better.&lt;/p&gt;&#10;&lt;p&gt;Normally, Termloop renders games in the way any other terminal application does. A cell is a rectangular shape, and has a background colour, a foreground colour, and a character. This means Termloop&amp;rsquo;s &amp;lsquo;pixels&amp;rsquo; are rectangles rather than squares. Here&amp;rsquo;s a screenshot from the PICO-8 game &lt;a href="http://www.lexaloffle.com/bbs/?tid=2145"&gt;Celeste&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2016/07/celeste.png" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Rendered using Termloop&amp;rsquo;s &lt;a href="https://github.com/JoelOtter/termloop/blob/master/_examples/image.go"&gt;image viewer&lt;/a&gt;, it would look like this:&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://www.joelotter.com/img/2016/07/celeste_before.png"&gt;&lt;img src="https://www.joelotter.com/img/2016/07/celeste_before.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;The image is very stretched, and we can only fit the lower half on-screen. What we can see, then, is that we&amp;rsquo;d ideally like to draw two square &amp;lsquo;pixels&amp;rsquo; in each rectangular terminal character.&lt;/p&gt;&#10;&lt;p&gt;One solution would be to ask users to fiddle with their font settings so that their font&amp;rsquo;s widths and heights were similar. Obviously this isn&amp;rsquo;t ideal. Instead, we can use &lt;a href="https://en.wikipedia.org/wiki/Block_Elements"&gt;Unicode block elements&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;▁ ▂ ▃ ▄ ▅ ▆ ▇ █&lt;/p&gt;&#10;&lt;p&gt;We want to fill half the terminal character with a different colour, so the one we want is called &amp;ldquo;lower half block&amp;rdquo;, and looks like this:&lt;/p&gt;&#10;&lt;p&gt;▄&lt;/p&gt;&#10;&lt;p&gt;Of course, there&amp;rsquo;s a tradeoff to be made. In using these half-block characters, we use up the text value for that terminal cell. This means that, when using pixel mode, a Termloop game will have double the screen height, but will &lt;em&gt;only&lt;/em&gt; be able to use cell background colours. For many games, this may well be worthwhile!&lt;/p&gt;&#10;&lt;h3 id="changes-to-termloop"&gt;Changes to Termloop&lt;/h3&gt;&#10;&lt;p&gt;The changes to Termloop&amp;rsquo;s code are fairly straightforward. First, a &lt;strong&gt;Screen&lt;/strong&gt; struct now contains a boolean which says whether or not it&amp;rsquo;s in pixel mode - the default is false.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Screen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ... other stuff ...&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pixelMode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;There&amp;rsquo;s also a new method, which a user should call right at the start of their program, before they call &lt;strong&gt;tl.Start()&lt;/strong&gt;.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// EnablePixelMode sets the screen to &amp;#39;pixel mode&amp;#39; - giving double&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// the canvas height while sacrificing character drawing ability.&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;EnablePixelMode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pixelMode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;Internally, the canvas needs to be represented as twice the height of the actual terminal. This is so developers can write their rendering code without needing to worry about whether or not they&amp;rsquo;re in pixel mode - as long as they&amp;rsquo;re just using &lt;strong&gt;Cell.Bg&lt;/strong&gt;, anyway. The &lt;strong&gt;Screen.Resize()&lt;/strong&gt; method is tweaked slightly.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pixelMode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;NewCanvas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// blah blah...&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;Finally, the rendering process while in pixel mode needs to be slightly different.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;termboxPixel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Canvas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cellBack&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cellFront&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&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="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;termj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sc"&gt;&amp;#39;\u2584&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cellFront&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;termbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetCell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;termj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;termbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cellFront&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bg&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;termbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cellBack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Bg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;Here, we step through the Cells in a Canvas column two at a time. We apply the background of the upper Cell as our terminal character background, and the background of the lower cell as the terminal character&amp;rsquo;s foreground colour. If the lower cell has no background colour, we don&amp;rsquo;t render the block character at all. This is because the &amp;lsquo;default&amp;rsquo; colour for a foreground is different from the default background, so rendering the character with colour &amp;lsquo;0&amp;rsquo; would result in a screen covered in stripes!&lt;/p&gt;&#10;&lt;h3 id="pixel-mode-in-action"&gt;Pixel mode in action&lt;/h3&gt;&#10;&lt;p&gt;To demonstrate the pixel mode, I&amp;rsquo;ve modified the Termloop &lt;a href="https://github.com/JoelOtter/termloop/blob/master/_examples/image.go"&gt;image viewer example&lt;/a&gt;, as this seems the most logical example to use pixel mode in. All that needed to be added was one line in &lt;strong&gt;main()&lt;/strong&gt;, like so:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Please provide a filepath to open&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewGame&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;EnablePixelMode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// This is the new line!&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BackgroundCanvasFromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;AddEntity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;NewImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;That Celeste screenshot? It now looks like this in the viewer:&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://www.joelotter.com/img/2016/07/celeste_after.png"&gt;&lt;img src="https://www.joelotter.com/img/2016/07/celeste_after.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;Much better! Everything is proportioned correctly, and we fit pretty much the whole image on-screen. If we tell the &lt;a href="https://github.com/JoelOtter/termloop/blob/master/_examples/pyramid.go"&gt;Pyramid example&lt;/a&gt; to use pixel mode, it looks like this:&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://www.joelotter.com/img/2016/07/pyramid.png"&gt;&lt;img src="https://www.joelotter.com/img/2016/07/pyramid.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;Notice that the text that&amp;rsquo;s normally in the top-left of the screen is gone - we can&amp;rsquo;t render it in pixel mode, though if we were brave enough we could draw it in pixels!&lt;/p&gt;&#10;&lt;p&gt;Finally, here&amp;rsquo;s the obligatory Tempa T.&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://www.joelotter.com/img/2016/07/newtempz.png"&gt;&lt;img src="https://www.joelotter.com/img/2016/07/newtempz.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;Termloop is open-source, and available on &lt;a href="https://github.com/JoelOtter/termloop"&gt;GitHub&lt;/a&gt;. You can have a chat to us on &lt;a href="https://gitter.im/JoelOtter/termloop"&gt;Gitter&lt;/a&gt; - we&amp;rsquo;re very friendly!&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>New site design</title><link>https://www.joelotter.com/posts/2016/07/new-site-design/</link><pubDate>Sat, 23 Jul 2016 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2016/07/new-site-design/</guid><description>&lt;p&gt;I&amp;rsquo;d got a little bit bored with the design on this website, and that boredom gave me no desire to write on it. Boo.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I&amp;rsquo;d got a little bit bored with the design on this website, and that boredom gave me no desire to write on it. Boo.&lt;/p&gt;&#10;&lt;p&gt;I was then reading about &lt;a href="http://brutalistwebsites.com/"&gt;brutalist web design&lt;/a&gt; (it is very cool) and decided that what I wanted was the most minimal site design possible while still maintaining readability. This is it, or at least as close as a couple of hours&amp;rsquo; work could get me.&lt;/p&gt;&#10;&lt;p&gt;The font is actually &lt;strong&gt;Courier&lt;/strong&gt;, a monospaced font. I don&amp;rsquo;t really like Courier as a terminal font - I prefer Source Code Pro or something similar - but I think it&amp;rsquo;s actually quite nice to read on a page. You may disagree, but I don&amp;rsquo;t care because it&amp;rsquo;s my website so there.&lt;/p&gt;&#10;&lt;p&gt;Being super-minimalist in code samples probably would have impacted readability, so I&amp;rsquo;ve just gone the opposite way and used a theme I really like, even if it&amp;rsquo;s quite colourful.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;isJavaScriptNice&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Why do you even need to ask&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;The source code is available on &lt;a href="https://github.com/JoelOtter/joelotter.github.io"&gt;GitHub&lt;/a&gt;, if for some reason you want to see it.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ll probably be making tweaks constantly over the next few days - suggestions in comments are very welcome.&lt;/p&gt;&#10;&lt;p&gt;For now, I&amp;rsquo;ve got a new fancy (as in, very much not fancy) home page. Go &lt;a href="https://www.joelotter.com/"&gt;there. Go on&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;P.S. Try looking at those &amp;lsquo;joel on stuff&amp;rsquo; arrays on the home page on a mobile. Cool, huh? Yet another reason to use monospaced fonts absolutely everywhere. Including on birthday cards.&lt;/p&gt;</content:encoded></item><item><title>It's time to stop pre-ordering</title><link>https://www.joelotter.com/posts/2015/11/stop-preordering/</link><pubDate>Sat, 07 Nov 2015 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2015/11/stop-preordering/</guid><description>&lt;p&gt;It&amp;rsquo;s been a little while since I was really excited for the release of a game. The last time was probably Halo 4&amp;rsquo;s release this time three years ago (for the record, it was disappointing), and the last time I pre-ordered a game was, I think, Mass Effect 3, six months earlier.&lt;/p&gt;</description><content:encoded>&lt;p&gt;It&amp;rsquo;s been a little while since I was really excited for the release of a game. The last time was probably Halo 4&amp;rsquo;s release this time three years ago (for the record, it was disappointing), and the last time I pre-ordered a game was, I think, Mass Effect 3, six months earlier.&lt;/p&gt;&#10;&lt;p&gt;There are a hell of a lot of advertisements on the Underground right now for Fallout 4. I&amp;rsquo;m not falling over myself to pick up a copy - I didn&amp;rsquo;t really get that into Fallout 3, didn&amp;rsquo;t play New Vegas at &lt;em&gt;all&lt;/em&gt;, and should really finish The Phantom Pain first - but the ads are somehow making me look forward to the release regardless. It&amp;rsquo;s out next week, and a few of my friends are making up for my relative lack of enthusiasm.&lt;/p&gt;&#10;&lt;blockquote class="twitter-tweet tw-align-center" lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;Crying because of the tease &lt;a href="https://t.co/AqkOd3RVcd"&gt;pic.twitter.com/AqkOd3RVcd&lt;/a&gt;&lt;/p&gt;&amp;mdash; Wiwa (@Wiwa44) &lt;a href="https://twitter.com/Wiwa44/status/662934946307272704"&gt;November 7, 2015&lt;/a&gt;&lt;/blockquote&gt;&#10;&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;&#10;&lt;blockquote class="twitter-tweet tw-align-center" lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;&lt;a href="https://twitter.com/Wiwa44"&gt;@Wiwa44&lt;/a&gt; &lt;a href="https://twitter.com/JoelOtter"&gt;@JoelOtter&lt;/a&gt; It&amp;#39;s so close! The hype is real!&lt;/p&gt;&amp;mdash; Neil Thompson (@NeilThompson_) &lt;a href="https://twitter.com/NeilThompson_/status/662963377623404544"&gt;November 7, 2015&lt;/a&gt;&lt;/blockquote&gt;&#10;&lt;script async src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;&#10;&lt;p&gt;With all the hype surrounding this game, it&amp;rsquo;s unsurprising that many people have pre-ordered it, whether on Steam, Amazon, or even by the old-fashioned method of wandering down to a GAME store and having them scribble your name in the pre-order ringbinder they keep under the counter.&lt;/p&gt;&#10;&lt;h3 id="why-though"&gt;Why, though?&lt;/h3&gt;&#10;&lt;p&gt;Pre-ordering was originally implemented to avoid falling victim to shortages - you&amp;rsquo;d pay in advance, and the shop or online retailer would keep a copy for you, guaranteeing that you wouldn&amp;rsquo;t have to wait for further shipments to arrive. In the past this was, predictably, very useful - remember the scramble trying to nab a copy of the new Harry Potter? However, in the age of digital distribution and ultra-cheap disc manufacture, the chances of failing to secure a copy of a game on release day are slim to none.&lt;/p&gt;&#10;&lt;p&gt;Why do we pre-order, then? Sure, there are pre-order bonuses - I remember pre-ordering Halo: Reach so I&amp;rsquo;d get the Recon helmet. Steam has its pre-load feature, which means there&amp;rsquo;s no waiting for a lengthy download when the game is released. I&amp;rsquo;d argue, though, that these bonuses are much too small to outweigh the fact that you&amp;rsquo;re effectively buying something you don&amp;rsquo;t even know you&amp;rsquo;ll like. Indeed, the only reason pre-orders still exist is not because they&amp;rsquo;re good for the consumer, but because they&amp;rsquo;re &lt;em&gt;fantastic&lt;/em&gt; for the publisher. Pre-orders are, I think, more than simply obsolete - they&amp;rsquo;re actively harmful to the video game industry. To explain why, let&amp;rsquo;s look at a few examples.&lt;/p&gt;&#10;&lt;h3 id="gothams-reckoning"&gt;Gotham&amp;rsquo;s reckoning&lt;/h3&gt;&#10;&lt;p&gt;Anyone who has any interest in gaming will undoubtedly be aware of the last five months&amp;rsquo; worth of controversy surrounding the &lt;a href="http://kotaku.com/batman-arkham-knights-pc-version-is-a-mess-1713351974"&gt;botched PC release&lt;/a&gt; of Batman: Arkham Knight. It&amp;rsquo;s been almost half a year since the game launched, and it&amp;rsquo;s looking like the PC version will &lt;a href="http://www.theverge.com/2015/11/2/9657734/batman-arkham-knight-pc-broken-refunds"&gt;never be fixed&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2015/11/batman.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;WB Games, the publisher, have a pretty decent track record on their PC ports - Shadow of Mordor and Arkham Origins were both great - so what could have prompted them to release a game they must have known was unplayable? The responsibility for the PC port was handed off to a studio other than Rocksteady themselves, but surely there must have been &lt;em&gt;some&lt;/em&gt; kind of quality control at the publisher level?&lt;/p&gt;&#10;&lt;p&gt;We can&amp;rsquo;t, of course, know the details of what was going on inside WB at the time. However, we do need to at least acknowledge the current-day climate that triple-A games are released into. They are often shipped unfinished, with day-one DLC and updates, and sometimes with bits of the game missing entirely - see Metal Gear Solid V&amp;rsquo;s multiplayer component as an example. In addition, there&amp;rsquo;s the fact that many players will have paid for the game already. Game broken on launch? Doesn&amp;rsquo;t matter if we&amp;rsquo;re still making money, right?&lt;/p&gt;&#10;&lt;p&gt;This led me to wonder how much money WB Games had likely pulled in through pre-orders before the game had even launched. Unfortunately, publishers don&amp;rsquo;t seem to release specific numbers for pre-order quantities, but VGChartz have a &lt;a href="http://www.vgchartz.com/preorders/42288/USA/"&gt;USA pre-order chart&lt;/a&gt; which gives some pretty good estimates.&lt;/p&gt;&#10;&lt;p&gt;Let&amp;rsquo;s try to make a rough guess as to how much was made off just USA pre-orders of Batman&amp;rsquo;s PC version. VGChartz gives us a figure of 40,566 units, which seems pretty reasonable. A new PC release in the States normally retails for around $60. Because we&amp;rsquo;re dealing with a PC game and it&amp;rsquo;s 2015, I&amp;rsquo;m going to assume that the overwhelming majority of sales were through digital distribution platforms like Steam, which typically take about a 30% cut of sales after tax. Using average USA sales tax, this gives us:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.0545&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mf"&gt;39.711&lt;/span&gt; &lt;span class="n"&gt;revenue&lt;/span&gt; &lt;span class="n"&gt;per&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;566&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mf"&gt;39.711&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;610&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;916.426&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="n"&gt;revenue&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;A little over 1.61 million dollars. That&amp;rsquo;s just in the US, just for pre-orders, and only for one version of the game - the console releases moved significantly higher quantities. I couldn&amp;rsquo;t find any figures on what this game&amp;rsquo;s budget was, but for &lt;em&gt;any&lt;/em&gt; game save Destiny this is what my brother refers to as &amp;ldquo;a big pile of money&amp;rdquo;. How many of these customers would have bought the game if they&amp;rsquo;d read a review? Significantly fewer, I&amp;rsquo;d bet.&lt;/p&gt;&#10;&lt;p&gt;We can clearly see that game pre-orders are hugely lucrative for publishers, and I believe this provides at least a small amount of incentive to cut a few corners in a game&amp;rsquo;s development. WB Games aren&amp;rsquo;t entirely to blame, though - over the last few years this has almost become a regular occurrence, and the more common it becomes, the more tolerant the market will be.&lt;/p&gt;&#10;&lt;p&gt;Let&amp;rsquo;s look at a few more examples. Please be aware I am far from a mathematician and working with limited sources, so these are very much estimates - I think they give a useful illustration, though.&lt;/p&gt;&#10;&lt;h3 id="remastered"&gt;Remastered?&lt;/h3&gt;&#10;&lt;p&gt;I don&amp;rsquo;t actually own an Xbox One, but my brother does, and so as a longtime Halo fan I was cautiously optimistic about Halo: The Master Chief Collection. At this point in time 343 Industries had released one decent remaster (Halo: Anniversary) and one okay sequel (Halo 4), and with the announcement that the old-style Halo ranking system would be making a return, I had reasonably high expectations for this release. Oh dear.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2015/11/halo-mcc.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p class="caption"&gt;My face when the first reviews started rolling in.&lt;/p&gt;&#10;&lt;p&gt;If Arkham Knight was 2015&amp;rsquo;s gaming disaster story, then 2014 very much belonged to The Master Chief Collection (2013 was, of course, SimCity). This game was &lt;em&gt;broken&lt;/em&gt;. As a whole it was coated with a smattering of small but irritating bugs, but the multiplayer component was pretty much unusable, even after a 20GB day-one patch. Ranking wasn&amp;rsquo;t switched on for a long time, and the matchmaking seemed to be totally unreliable. The community on the Halo subreddit had an active &lt;a href="https://www.reddit.com/r/halo/comments/2lynlg/okay_r_halo_enough_with_the_h5_hypehate_we_just/"&gt;bug list&lt;/a&gt; and, to 343i&amp;rsquo;s credit, they did seem to be fairly good at engaging with the community. The fact remains, though, that a very obviously broken game was shipped, and the result was that the MCC&amp;rsquo;s multiplayer was never really allowed to gain the momentum that a collection of such classics deserved.&lt;/p&gt;&#10;&lt;p&gt;I finally managed to play the game when I went home for Christmas, and had a great time playing through the campaigns on split-screen with my brother. Unfortunately it looks like we &lt;a href="http://www.polygon.com/2015/7/30/9071137/halo-5-no-split-screen"&gt;won&amp;rsquo;t be able to do that&lt;/a&gt; with Halo 5 this year - this change is actually what caused us not to buy the game. I am not enamoured with 343 Industries at present.&lt;/p&gt;&#10;&lt;p&gt;Were 343i and Microsoft punished for this negligence by poor sales? Nah. Of course not. The Master Chief Collection has pushed an estimated 5 million units to date. How many of these were pre-orders?&lt;/p&gt;&#10;&lt;p&gt;VGChartz gives us a figure of 346,452 copies pre-ordered prior to launch in the USA. Let&amp;rsquo;s be pessimistic and assume that the overwhelming majority of these sales were disc-based. I think &lt;a href="https://www.quora.com/To-what-extent-are-video-game-retailers-threatened-by-digital-distribution/answer/Helen-Zhang-3"&gt;this Quora answer&lt;/a&gt; gives a reasonable estimate for the revenue split for a disc sale - 45% going to the publisher and developer, who are effectively one and the same in this case. In reality this is likely a very conservative estimate, as many copies of the MCC were sold digitally or as pack-ins with Xbox One consoles, my brother&amp;rsquo;s copy included.&lt;/p&gt;&#10;&lt;p&gt;The Master Chief Collection retailed at $50. Assuming the same sales tax as before, we can calculate:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.0545&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.45&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mf"&gt;21.27375&lt;/span&gt; &lt;span class="n"&gt;revenue&lt;/span&gt; &lt;span class="n"&gt;per&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="mi"&gt;346&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;452&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mf"&gt;27.27375&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;370&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;333.235&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="n"&gt;revenue&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;That&amp;rsquo;s well over 7 million dollars in US pre-order revenue alone, at a conservative estimate. There aren&amp;rsquo;t any figures around for the Master Chief Collection&amp;rsquo;s budget - Halo 3 cost around $60m, with Halo 4 costing around $80-100m, so I would guess the answer is somewhere in between. Either way, US pre-order revenue may have accounted for a good tenth of the game&amp;rsquo;s budget. That&amp;rsquo;s significant.&lt;/p&gt;&#10;&lt;p&gt;Still unconvinced? Let&amp;rsquo;s look at one more example.&lt;/p&gt;&#10;&lt;h3 id="you-will-spend-because-we-demand-it"&gt;You will spend because we demand it&lt;/h3&gt;&#10;&lt;p&gt;Let me be clear - I loved Mass Effect 3. Yes, the ending was daft, no, it wasn&amp;rsquo;t quite as good as its predecessor, but it was still a really enthralling RPG, of the kind only BioWare seem to make. It wasn&amp;rsquo;t a broken game, though they did &amp;ldquo;patch&amp;rdquo; that ending shortly after release, but it&amp;rsquo;s still worth looking at because of the sheer number of pre-order sales.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2015/11/ftl.png" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Ignoring the Wii U version, which was released later, VGChartz lists a total of 1,339,052 pre-orders in the USA across Xbox, PlayStation and PC. That&amp;rsquo;s over a million people who bought this game without knowing if it would even be good, myself included - I don&amp;rsquo;t know if that&amp;rsquo;s a testament to the strength of the series, the players&amp;rsquo; trust in BioWare, or some really strong marketing. Either way, it&amp;rsquo;s a hell of a lot of sales.&lt;/p&gt;&#10;&lt;p&gt;Let&amp;rsquo;s assume each unit sold for $60, and EA/BioWare got a 45% cut of each sale. The PC version was likely cheaper, but on digital distribution platforms the sales cut would have been much higher. Indeed, copies sold on EA&amp;rsquo;s Origin service wouldn&amp;rsquo;t have had to pay the distributor at all - this is probably quite a conservative estimate. Assuming the same sales tax again, we end up with:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.0545&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.45&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mf"&gt;25.5285&lt;/span&gt; &lt;span class="n"&gt;revenue&lt;/span&gt; &lt;span class="n"&gt;per&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;339&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;052&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mf"&gt;25.5285&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;183&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;988.982&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="n"&gt;revenue&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;That&amp;rsquo;s over 34 million dollars on USA pre-orders alone. That&amp;rsquo;s not a big pile of money, that&amp;rsquo;s an &lt;em&gt;enormous&lt;/em&gt; pile of money.&lt;/p&gt;&#10;&lt;p&gt;Mass Effect 3&amp;rsquo;s budget has been estimated at around $40 million. If this is accurate, it means that world pre-order sales probably covered the budget; the game had paid for itself before it was even released. Looking at it this way, it&amp;rsquo;s hardly surprising publishers are so ravenously keen on getting us to pre-order.&lt;/p&gt;&#10;&lt;h3 id="you-need-me-man-i-dont-need-you"&gt;You need me, man, I don&amp;rsquo;t need you&lt;/h3&gt;&#10;&lt;p&gt;Game pre-ordering is a relic we need to do away with as soon as possible. In fact, it&amp;rsquo;s more than just obsolete - if it encourages the kind of corner-cutting we&amp;rsquo;ve seen in some games, then it&amp;rsquo;s actively harmful to the industry. It&amp;rsquo;s anti-consumer. It needs to stop - but it won&amp;rsquo;t until we stop falling for it.&lt;/p&gt;&#10;&lt;p&gt;If you absolutely must pre-order, whether for Steam pre-load or to get bonuses, at least wait until the review embargo has lifted. This is usually a few days before release, and means you&amp;rsquo;re not flying totally blind when you buy something; if a game is unplayably broken, you&amp;rsquo;ll know not to spend your hard-earned moolah on it.&lt;/p&gt;&#10;&lt;p&gt;If all else fails, just wait a few days after launch. You won&amp;rsquo;t miss much. Promise.&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;Thanks to &lt;a href="http://twitter.com/wiwa44"&gt;Wiwa&lt;/a&gt; and &lt;a href="http://twitter.com/neilthompson_"&gt;Neil&lt;/a&gt; for letting me embed their tweets. Wiwa wishes it to be known that he is firmly anti-pre-order. He is aware that he is a hypocrite. He&amp;rsquo;s still excited about Fallout 4, though.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>A DJ Khaled Tinder bot</title><link>https://www.joelotter.com/posts/2015/05/dj-khaled-tinder-bot/</link><pubDate>Sun, 17 May 2015 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2015/05/dj-khaled-tinder-bot/</guid><description>&lt;p&gt;I had great fun last month writing a Reddit bot that pretended to be DJ Khaled. It&amp;rsquo;s been running ever since, but I thought it was about time to extend Khaled&amp;rsquo;s internet empire into another social media service. The obvious candidate: Tinder.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I had great fun last month writing a Reddit bot that pretended to be DJ Khaled. It&amp;rsquo;s been running ever since, but I thought it was about time to extend Khaled&amp;rsquo;s internet empire into another social media service. The obvious candidate: Tinder.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2015/05/tinder.png" alt=""&gt;{: .phone-shot}&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m not going to spend much time explaining what I want the bot to be or to do - it&amp;rsquo;ll be pretty much the same as the Reddit bot. If you haven&amp;rsquo;t read &lt;a href="http://joelotter.com/2015/04/01/dj-khaled-reddit-bot.html"&gt;that post&lt;/a&gt;, I recommend you do so. It&amp;rsquo;s great, honest. Someone told me I&amp;rsquo;d &amp;ldquo;won the internet&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;Up to speed? Cool. On to Tinder. Tinder is a weird little app. There&amp;rsquo;s no public-facing API to speak of, and creating anything that talks to the service is unofficially frowned-upon - which only makes it that much more fun to work with. There have been a few community efforts to get a library put together, and &lt;a href="https://gist.github.com/rtt/10403467"&gt;this Gist&lt;/a&gt; gives some really nice documentation on which different endpoints are available.&lt;/p&gt;&#10;&lt;p&gt;I like working with a library, so I settled on a Python one called &lt;a href="https://github.com/charliewolf/pynder"&gt;Pynder&lt;/a&gt;. It&amp;rsquo;s a little buggy, but I like the way it&amp;rsquo;s designed, so thought I&amp;rsquo;d stick with it. I&amp;rsquo;m largely working with &lt;a href="https://github.com/JoelOtter/pynder"&gt;my fork&lt;/a&gt;, and hopefully I&amp;rsquo;ll get some pull requests accepted.&lt;/p&gt;&#10;&lt;p&gt;Using Pynder, it&amp;rsquo;s fairly trivial to connect to Tinder and get our swipe on, like so:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pynder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FACEBOOK_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FACEBOOK_AUTH_TOKEN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nearby_users&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;like&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# Khaled&amp;#39;s a little desperate.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;The variable &lt;em&gt;FACEBOOK_ID&lt;/em&gt; is simply the ID for the Facebook account we&amp;rsquo;ve created to connect to Tinder with - unfortunately, the only way to make a fake DJ Khaled Tinder account is to make a matching Facebook one. I&amp;rsquo;ll just have to hope it doesn&amp;rsquo;t get nerfed as a spam account.&lt;/p&gt;&#10;&lt;p&gt;The variable &lt;em&gt;FACEBOOK_AUTH_TOKEN&lt;/em&gt; presents us with more of a problem. This is the token used to authenticate with the Facebook API, and is specific to the Tinder app. It&amp;rsquo;s possible to get a valid auth token using the web API, by following &lt;a href="https://www.facebook.com/dialog/oauth?client_id=464891386855067&amp;amp;redirect_uri=https://www.facebook.com/connect/login_success.html&amp;amp;scope=basic_info,email,public_profile,user_about_me,user_activities,user_birthday,user_education_history,user_friends,user_interests,user_likes,user_location,user_photos,user_relationship_details&amp;amp;response_type=token"&gt;this link&lt;/a&gt; and then extracting the token from the &lt;em&gt;location&lt;/em&gt; header of the response, once you&amp;rsquo;ve granted Tinder the permissions it needs.&lt;/p&gt;&#10;&lt;p&gt;Unfortunately, this token is only valid for, at most, a couple of hours. This isn&amp;rsquo;t really useful for a long-running bot we want to deploy to some server somewhere and forget about. Facebook offers longer-lasting auth tokens, which have an expiry time of around 60 days. This is what the Tinder app uses.&lt;/p&gt;&#10;&lt;p&gt;Sounds perfect, right? I thought the same. Facebook&amp;rsquo;s documentation &lt;a href="https://developers.facebook.com/docs/facebook-login/access-tokens#extending"&gt;describes how&lt;/a&gt; to turn a standard auth token into an extended one, by use of an API request. Unfortunately, this request requires us to send our app&amp;rsquo;s secret token - in the case of Tinder, this is something we don&amp;rsquo;t really have access to.&lt;/p&gt;&#10;&lt;p&gt;So, have we hit a dead end with regard to getting a token? Not quite. Let&amp;rsquo;s forget about talking to Facebook for now. What if we could somehow grab the token that the Android app is using to communicate with Facebook? Doing this is a bit fiddly, but actually pretty quick. Here&amp;rsquo;s how.&lt;/p&gt;&#10;&lt;h3 id="my-mate-charles"&gt;My mate Charles&lt;/h3&gt;&#10;&lt;p&gt;My laptop is running OS X and my phone is running Android, so this process might be a little specific. However, the general principles should apply with whatever weird combinations of tech you&amp;rsquo;ve got.&lt;/p&gt;&#10;&lt;p&gt;On my computer, I installed a web debugging proxy called &lt;a href="http://www.charlesproxy.com/"&gt;Charles&lt;/a&gt;. It seems to be pretty powerful, but I&amp;rsquo;m only really scratching the surface of its features for this. It&amp;rsquo;s got a 30-day free trial and some mildly irritating pop-up &amp;ldquo;buy me&amp;rdquo; messages, which I guess is fair enough.&lt;/p&gt;&#10;&lt;p&gt;First of all, we need to set up the proxy to run on a specific port on this machine, as well as enable SSL proxying. These settings live in the Proxy menu and should be easy enough to find. Here are the settings I used:&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2015/05/http.png" alt=""&gt;{: .center-image}&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2015/05/ssl.png" alt=""&gt;{: .center-image}&lt;/p&gt;&#10;&lt;p&gt;Once we&amp;rsquo;ve got our proxy server up and running, we need to get the Android phone to connect to it. I used &lt;a href="https://play.google.com/store/apps/details?id=org.proxydroid&amp;amp;hl=en_GB"&gt;Proxydroid&lt;/a&gt;, which seems to work pretty well. Just point it at Charles and flip the switch to &amp;lsquo;on&amp;rsquo;. I set it up like this:&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2015/05/proxydroid.png" alt=""&gt;{: .center-image .phone-shot}&lt;/p&gt;&#10;&lt;p&gt;We&amp;rsquo;re not quite done yet, though. We still need to get a certificate onto the phone so that Charles can decode the encrypted traffic. Thankfully, Charles provides us with a very simple method to get - with your proxy connected, point your phone&amp;rsquo;s browser at &lt;a href="http://www.charlesproxy.com/getssl"&gt;http://www.charlesproxy.com/getssl&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;Once that&amp;rsquo;s installed, run Tinder on the phone, then have a look at what Charles spits out. Tinder seems to talk to a lot of different services, but the request we want is to the endpoint &amp;lsquo;auth&amp;rsquo;. It should look like this:&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2015/05/token.png" alt=""&gt;{: .center-image}&lt;/p&gt;&#10;&lt;p&gt;And there&amp;rsquo;s our auth token! Go ahead and copy that, we&amp;rsquo;re going to need it.&lt;/p&gt;&#10;&lt;h3 id="getting-chatty"&gt;Getting chatty&lt;/h3&gt;&#10;&lt;p&gt;The basic function of our bot is that, once an hour, it will:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Check Tinder to see if it can swipe, and if so, swipe right on every single person until we hit the 12-hour limit. I guess I could buy the bot Tinder Pro or whatever it&amp;rsquo;s called, but the thought of being one of those people who pays for Tinder frightens me a little bit. Even if it&amp;rsquo;s for Khaled.&lt;/li&gt;&#10;&lt;li&gt;Go through everyone the bot has matched with, and progress any conversations appropriately. Have a look at the &lt;a href="http://joelotter.com/2015/04/01/dj-khaled-reddit-bot.html"&gt;Reddit bot post&lt;/a&gt; if you don&amp;rsquo;t know how a Khaled chat goes.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Basically, that&amp;rsquo;s just:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;handle_likes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;handle_matches&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;Let&amp;rsquo;s go through each of those methods. First up is doing what Tinder&amp;rsquo;s known for&amp;hellip;&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_likes&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nearby_users&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Tinder Team&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Out of swipes, pausing one hour...&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;like&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Liked &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;Here, we want to continuously fetch nearby users and &amp;rsquo;like&amp;rsquo; all of them. Khaled ain&amp;rsquo;t shallow. Pynder sometimes fails to parse the JSON received, so catching that &lt;em&gt;ValueError&lt;/em&gt; lets us just continue as normal. If we see a user called &amp;lsquo;Tinder Team&amp;rsquo;, that&amp;rsquo;s the Tinder service&amp;rsquo;s way of letting us know we&amp;rsquo;re out of swipes, so it&amp;rsquo;s only then that we stop.&lt;/p&gt;&#10;&lt;p&gt;Khaled got nine matches this morning when this ran. Six of them then unmatched him. So shallow.&lt;/p&gt;&#10;&lt;p&gt;Now that we&amp;rsquo;ve got a few people to chat to, we can do so. Here&amp;rsquo;s what that method looks like:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_matches&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; matches&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;Simple enough - grab any and all matches, and message them appropriately. The main chunk of work is done in the &lt;em&gt;message&lt;/em&gt; method, which looks like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;messages&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;khaled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;match&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;said&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;person&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;from&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;khaled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;said&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;dj khaled&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;message&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;said&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Finished conversation with &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;said&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;No new messages from &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;First up, if we&amp;rsquo;ve never spoken to the person, we should send Khaled&amp;rsquo;s traditional greeting. Otherwise, we want to go through the message log, and check if Khaled&amp;rsquo;s potential true love has mentioned &amp;lsquo;DJ Khaled&amp;rsquo; since the last time he replied. If so, we send the next message(s) in the list. If there are no new messages from the person, or they haven&amp;rsquo;t said the right thing, we ignore &amp;rsquo;em.&lt;/p&gt;&#10;&lt;p&gt;The &lt;em&gt;send&lt;/em&gt; method is pretty simple. Here it is:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message_no&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;message_no&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/user/matches/&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Sent message &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message_no&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; to &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;person&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;Pynder has its own send method, but it&amp;rsquo;s a little broken and uses the wrong ID, hence this slight workaround. This method also stalls three seconds between sending consecutive messages - I reckon this seems a little more believable.&lt;/p&gt;&#10;&lt;p&gt;Tinder doesn&amp;rsquo;t really support formatting, and it definitely isn&amp;rsquo;t compatible with ChangeTip, so this bot can&amp;rsquo;t actually give people money. However, Tinder &lt;em&gt;does&lt;/em&gt; support Unicode, so we can send Emojis. Yay! The &lt;em&gt;messages.py&lt;/em&gt; file looks like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# coding=utf8&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Say my name, baby.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Say my NAME, baby.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;You smart. You loyal. You grateful. I appreciate that.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;💷 Go buy your mom a house.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;💷 Go buy your whole family houses.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;💷 Put this money in your savings account.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;💷 Go spend some money for no reason.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;💷 Come back and ask for more.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;Baby, let the music take control.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Let we the best sound take control, baby - hold on, say my name?&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;S&amp;#39;right. That&amp;#39;s right, baby. You remember that.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;https://youtu.be/fxPBu_vX9Q0&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;Yeah&amp;hellip;having Emojis in the middle of code looks &lt;em&gt;really&lt;/em&gt; weird.&lt;/p&gt;&#10;&lt;p&gt;We&amp;rsquo;re pretty much done with the coding side of things now. If you&amp;rsquo;d like to look at the full source, it&amp;rsquo;s &lt;a href="https://github.com/JoelOtter/khaled-tinder"&gt;on my GitHub&lt;/a&gt;. Fill yer boots.&lt;/p&gt;&#10;&lt;p&gt;Does it work? You bet. Here are some screenshots of its first ever conversation - it was lucky enough to meet someone who actually got the joke. These shots are from before the line breaks in messages were split up into individual ones, but it&amp;rsquo;s pretty much the same.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2015/05/tinder1.png" alt=""&gt;{: .phone-shot}&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2015/05/tinder2.png" alt=""&gt;{: .phone-shot}&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2015/05/tinder3.png" alt=""&gt;{: .phone-shot}&lt;/p&gt;&#10;&lt;p&gt;Big up this person for getting the joke and playing along.&lt;/p&gt;&#10;&lt;p&gt;Aaaand, that&amp;rsquo;s pretty much it! The bot&amp;rsquo;s been deployed into the cloud (I love that silly phrase), and I&amp;rsquo;ll probably &lt;a href="http://www.twitter.com/JoelOtter"&gt;tweet&lt;/a&gt; if it does anything totally hilarious. For now, here are some links you might find useful:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://gist.github.com/rtt/10403467"&gt;Tinder API&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/charliewolf/pynder"&gt;Pynder&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/JoelOtter/khaled-tinder"&gt;The source code for this bot&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://instagram.com/p/cf32KWru14/"&gt;DJ Khaled approving the most powerful servers&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Until next time - remember to let the music take control.&lt;/p&gt;</content:encoded></item><item><title>A DJ Khaled Reddit bot</title><link>https://www.joelotter.com/posts/2015/04/dj-khaled-reddit-bot/</link><pubDate>Wed, 01 Apr 2015 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/posts/2015/04/dj-khaled-reddit-bot/</guid><description>&lt;p&gt;I&amp;rsquo;d wanted to make a Reddit bot for a little while, really just as a toy project, and finding the excellent &lt;a href="http://praw.readthedocs.org/en/latest/index.html"&gt;PRAW&lt;/a&gt; library for Python (as well as &lt;em&gt;finally&lt;/em&gt; having some time off) gave me the kick I needed to get it done. It&amp;rsquo;s also April Fool&amp;rsquo;s Day, so I thought I&amp;rsquo;d make something a bit stupid.&lt;/p&gt;</description><content:encoded>&lt;p&gt;I&amp;rsquo;d wanted to make a Reddit bot for a little while, really just as a toy project, and finding the excellent &lt;a href="http://praw.readthedocs.org/en/latest/index.html"&gt;PRAW&lt;/a&gt; library for Python (as well as &lt;em&gt;finally&lt;/em&gt; having some time off) gave me the kick I needed to get it done. It&amp;rsquo;s also April Fool&amp;rsquo;s Day, so I thought I&amp;rsquo;d make something a bit stupid.&lt;/p&gt;&#10;&lt;p&gt;I decided to make a bot that would simulate a conversation with the wonderful racounteur that is DJ Khaled. If you don&amp;rsquo;t know what I&amp;rsquo;m talking about, watch this video (the second half is the important bit):&lt;/p&gt;&#10;&lt;div class="video"&gt;&#10; &lt;iframe src="https://www.youtube.com/embed/xR4Ej7bny8k?start=24s" frameborder="0" allowfullscreen&gt;&#10; &lt;/iframe&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Cool guy, huh? My idea was that the bot would monitor the top X number of threads on /r/all, and look through the comments to see if anyone mentioned our pal Khaled&amp;rsquo;s name. If so, they&amp;rsquo;d engage them in the conversation from the video.&lt;/p&gt;&#10;&lt;p&gt;If our player got through the conversation, they&amp;rsquo;d be rewarded with a small amount of money, so they could &amp;ldquo;go buy your mom a house&amp;rdquo;. By small I mean &lt;em&gt;small&lt;/em&gt; - I&amp;rsquo;m using ChangeTip to send 5 bits, which at the time of writing is equal to £0.00083. Someone&amp;rsquo;s mom might not get to move for a little bit longer.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m going to dive into how I made the thing, but if your time&amp;rsquo;s a bit limited, you can see the source code &lt;a href="http://github.com/JoelOtter/khaled-bot"&gt;on my GitHub&lt;/a&gt;, and here&amp;rsquo;s a screenshot of what the finished conversation looks like.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2015/04/conv.png" alt=""&gt;{: .center-img}&lt;/p&gt;&#10;&lt;h2 id="implementation"&gt;Implementation&lt;/h2&gt;&#10;&lt;p&gt;The bot is built in Python, using PRAW, which provides a very nice wrapper for the Reddit API. We start out by initializing the Reddit object and logging in, like so:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;praw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Reddit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;My cool Reddit bot 0.0.1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;The string given to the Reddit constructor is the User-Agent field for the Reddit API. The API eschews API tokens and the like - instead, individual users are tracked by the User-Agent string. Reddit are quite strict about applications being honest with this parameter - have a look at their &lt;a href="https://github.com/reddit/reddit/wiki/API"&gt;documentation&lt;/a&gt; if you&amp;rsquo;re interested.&lt;/p&gt;&#10;&lt;p&gt;The actual crawling of Reddit comments is quite simple using PRAW. Here&amp;rsquo;s a stripped-down example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;subr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_subreddit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;all&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;subr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_hot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MoreComments&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;dj khaled&amp;#34;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Say my name, baby.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;This code will fetch the subreddit /r/all. It then goes through the top 100 submissions in that subreddit, and for each one looks through the top comments. If it finds a case-insensitive reference to DJ Khaled in a comment, it replies to it and stops looking through that submission.&lt;/p&gt;&#10;&lt;p&gt;The number of comments actually returned is a maximum of 200, but not all of them may be usable. Instead, some of them may be &lt;code&gt;MoreComments&lt;/code&gt; objects - these are used to fetch further comments. We ignore these. Reddit&amp;rsquo;s API is quite strict with rate limiting - they quote around 30 requests a minute, which is not very many - so it makes sense to only fetch what we need.&lt;/p&gt;&#10;&lt;p&gt;Our bot runs in an infinite loop, which looks like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;process_queue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;process_new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;HTTPError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;The bot will run once a minute. An &lt;code&gt;HTTPError&lt;/code&gt; exception will be caught if it fails to connect to Reddit - this could be because Reddit is down, or because their servers are busy (the 503 error we all know and love). If this happens, we simply give up on the current iteration and start again.&lt;/p&gt;&#10;&lt;p&gt;The &lt;code&gt;process_new()&lt;/code&gt; method does pretty much what I outlined above, with a few additions:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_new&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;subr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_subreddit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;all&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;subr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_hot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permalink&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MoreComments&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;dj khaled&amp;#34;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;newc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Say my name, baby.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;RateLimitExceeded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permalink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./seen.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;s_f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;s_f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permalink&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;queue&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&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="n"&gt;newc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permalink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;New comment &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;newc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permalink&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;As well as replying, we add our reply comment to a queue, which is processed by the &lt;code&gt;process_queue()&lt;/code&gt; method. There are actually three queues - one for each stage of the conversation.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;The &lt;code&gt;process_new()&lt;/code&gt; method also keeps track of seen submissions. This ensures that we only start one conversation on each submission - we need to stop people abusing our bot for free money, after all. Even DJ Khaled isn&amp;rsquo;t infinitely rich. This list is also written to a file, so that it can persist if the bot crashes or needs to be restarted for some reason.&lt;/p&gt;&#10;&lt;p&gt;The &lt;code&gt;RateLimitExceeded&lt;/code&gt; exception is thrown if we try to comment too often. How often a Reddit account is allowed to comment depends on the amount of karma it has - to start with, one can only comment once every five minutes. This makes our bot a bit slow, but for our purposes this doesn&amp;rsquo;t really matter, and hopefully it will speed up if people upvote the stuff it comes out with.&lt;/p&gt;&#10;&lt;p&gt;The &lt;code&gt;process_queue()&lt;/code&gt; method looks like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_queue&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;[&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;com&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_submission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comments&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rep&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;dj khaled&amp;#34;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rep&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;newc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rep&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;RateLimitExceeded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&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="n"&gt;newc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permalink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Repled with comment &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;newc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permalink&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;This code will process five items from each queue - this is to keep our number of requests down. The code fetches the comment using PRAW, and checks it to see if any of the replies contain DJ Khaled&amp;rsquo;s name. If so, we reply to it with the appropriate response, and move the reply comment into the next queue if there is one, removing the old comment from its queue. In this way we gradually move conversations along.&lt;/p&gt;&#10;&lt;p&gt;The responses are stored in their own Python file:&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;responses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;Say my _name_, baby!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;You smart. You loyal. You grateful. I _appreciate_ that.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Go buy your mom a house. &#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Go buy your whole family houses. &#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Put this money in your savings account. &#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Go spend some money for no reason. &#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Come back and ask for more. &#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;/u/changetip 5 bits&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Baby, let the music take control. &#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;Let we the best sound take control, baby - hold on, say my name?&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;S&amp;#39;right. That&amp;#39;s right, baby. You remember that. &#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;https://youtu.be/fxPBu_vX9Q0&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;---&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;^_[Confused?](http://www.joelotter.com)_&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;&#10;&lt;p&gt;I did this rather than putting them in a plain text file so as to use Python&amp;rsquo;s multiline strings - I had some issues with getting newlines to behave properly simply using &lt;code&gt;\n&lt;/code&gt;, which I suspect has to do with Reddit&amp;rsquo;s Markdown processing.&lt;/p&gt;&#10;&lt;p&gt;When we put it all together, the completed bot looks like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;praw.errors&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RateLimitExceeded&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;requests.exceptions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HTTPError&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;responses&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;praw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Reddit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;User-Agent string goes here&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_queue&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;[&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;com&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_submission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comments&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rep&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;dj khaled&amp;#34;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rep&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;newc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rep&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;RateLimitExceeded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&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="n"&gt;newc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permalink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Repled with comment &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;newc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permalink&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_new&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;subr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_subreddit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;all&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;subr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_hot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permalink&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MoreComments&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;dj khaled&amp;#34;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;newc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Say my name, baby.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;RateLimitExceeded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permalink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./seen.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;s_f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;s_f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permalink&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;queue&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&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="n"&gt;newc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permalink&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;New comment &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;newc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;permalink&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Initialise&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;./seen.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;process_queue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;process_new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;HTTPError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;And that&amp;rsquo;s pretty much it - it doesn&amp;rsquo;t take much! If you&amp;rsquo;re thinking of building a Reddit bot, here are some links that might be useful:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="http://praw.readthedocs.org/en/latest/index.html"&gt;PRAW&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/reddit/reddit/wiki/API"&gt;Reddit API docs&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/JoelOtter/khaled-bot"&gt;The source code for this bot&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://instagram.com/p/cf32KWru14/"&gt;DJ Khaled approving the most powerful servers&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="http://www.reddit.com/u/TheRealDJKhaled"&gt;The actual bot on Reddit&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Any questions, don&amp;rsquo;t hesitate to drop a comment in the box below. Go buy your mom a house.&lt;/p&gt;</content:encoded></item><item><title>Pixels Are Evil</title><link>https://www.joelotter.com/pixels-are-evil/</link><pubDate>Tue, 30 Sep 2014 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/pixels-are-evil/</guid><description>&lt;p&gt;The game development landscape has changed significantly over the last few years (understatement of the century, but whatever). One of the most significant of these changes , especially for game developers - though it&amp;rsquo;s one that often goes unnoticed - is the rise of screen resolution and aspect ratio fragmentation.&lt;/p&gt;</description><content:encoded>&lt;p&gt;The game development landscape has changed significantly over the last few years (understatement of the century, but whatever). One of the most significant of these changes , especially for game developers - though it&amp;rsquo;s one that often goes unnoticed - is the rise of screen resolution and aspect ratio fragmentation.&lt;/p&gt;&#10;&lt;p&gt;Go back a few years&amp;hellip;let&amp;rsquo;s call it 2010, shall we? The Nintendo DS was on the way out and the iPhone 4 was, sadly, the current pinnacle of handheld gaming. TV screens and monitors had a resolution that varied a bit (though largely stuck at the &lt;a href="http://tiamat.tsotech.com/hd-sucks"&gt;awfully termed&lt;/a&gt; &amp;ldquo;high definition&amp;rdquo;, but were mostly either 16:9 or 4:3. Android was kicking about, but wasn&amp;rsquo;t really being taken seriously as a viable gaming platform just yet, and there weren&amp;rsquo;t an overwhelmingly huge number of screen sizes out there.&lt;/p&gt;&#10;&lt;p&gt;In short - game developers, by and large, knew what screen size they were targeting when doing all the graphics-y bits of the game. Unless you&amp;rsquo;re currently one of the indie developers heavily immersed in 3DS development (hint: you&amp;rsquo;re probably not), this is something that really does need to be taken into consideration. And it&amp;rsquo;s not just Android now (yeah, I heard you, jeering Apple fanboys) - there are now several different screen resolutions of iPhone and iPad, as well as a couple of different aspect ratios.&lt;/p&gt;&#10;&lt;p&gt;One solution would be to make a very slightly different version of the game for each viable screen ratio and just scale the resolution. Does this sound elegant? Didn&amp;rsquo;t think so. Ideally, we want to write one game that will look at least &lt;em&gt;acceptable&lt;/em&gt; on even the strangest screen ratios and resolutions - we can probably stipulate &amp;ldquo;it&amp;rsquo;s landscape&amp;rdquo;, but not a huge amount more. We&amp;rsquo;re going to run into a few issues we&amp;rsquo;ll need to think about:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;What happens if the screen is really small but with a really high resolution, or the opposite?&lt;/li&gt;&#10;&lt;li&gt;How do we handle a change in aspect ratio - do we stretch the viewport or cut bits off/add bits on?&lt;/li&gt;&#10;&lt;li&gt;What happens if we&amp;rsquo;re using something like a physics engine and need to translate coordinates?&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;(I&amp;rsquo;m thinking mainly about 2D games here. 3D devs, you do your thing, you gods among men.)&lt;/p&gt;&#10;&lt;p&gt;For me, one of the best things to change in the ol&amp;rsquo; mind-brain is to &lt;em&gt;stop thinking in terms of pixels&lt;/em&gt;!&lt;/p&gt;&#10;&lt;p&gt;Okay, so that might be a bit harsh - your artists will be drawing in terms of pixels, textures and the like are probably measured in pixels, and you will need pixels when working out how to render those bloody fonts. In terms of the placement and size of your in-game entities, though, it&amp;rsquo;s a unit of measurement you should stay well clear of.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;m going to outline one solution I used recently, focusing mainly on those last two bullet points. I&amp;rsquo;m using &lt;a href="http://libgdx.badlogicgames.com"&gt;LibGDX&lt;/a&gt; (which is brilliant) as my graphics library, and the excellent &lt;a href="http://box2d.org/about"&gt;Box2D&lt;/a&gt; for physics, but with a bit of luck this should be at least slightly helpful right across the (2D, as always) board. Bear in mind the usual disclaimer: this isn&amp;rsquo;t &lt;em&gt;the&lt;/em&gt; solution, nor is it necessarily the &lt;em&gt;best&lt;/em&gt; solution, but it&amp;rsquo;s one I found easy to implement, and, well, it works for me. Cool? Cool.&lt;/p&gt;&#10;&lt;p&gt;One crucial issue I ran into using these two libraries in tandem is that LibGDX likes to draw in terms of pixels, as it wraps OpenGL. Box2D, being a physics library, relies on real-world measurements like metres and kilograms; yards and pounds don&amp;rsquo;t get a look-in. Praise be to Erin Catto.&lt;/p&gt;&#10;&lt;p&gt;So - we don&amp;rsquo;t want to think in terms of pixels, so what should we use instead? Well, the metric units are right there thanks to Box2D - it makes sense to use those. This means your in-game entities will define their dimensions and positions in terms of metres. You&amp;rsquo;ll also define the size of the viewport itself in metres.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve decided that I prefer the approach of resizing the viewport in an intelligent way, as opposed to stretching it. This is at the cost of losing a little bit of screen, but this can be adjusted afterwards anyway, by moving the camera. If you prefer the stretching approach, hopefully the differences in implementation should be easy enough to work out.&lt;/p&gt;&#10;&lt;p&gt;Our first step is to pick a default resolution. This isn&amp;rsquo;t &lt;em&gt;hugely&lt;/em&gt; important, but provides a baseline we can do our scaling from. I primarily work with Android, so I reckon 1280*720 is a good initial target - it&amp;rsquo;s used by quite a few popular phones. You&amp;rsquo;ll probably want to choose your own - just pick whatever you think best fits your use case.&lt;/p&gt;&#10;&lt;p&gt;Next up, we&amp;rsquo;ve got to pick a default pixel-to-unit ratio, letting us define how big objects will appear on the screen, as well as the size of the viewport in our chosen units (metres, in my case).&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve been tempted in the past to use a scaling of 1px/m, just to save time and so that there&amp;rsquo;s a one-to-one mapping. Don&amp;rsquo;t do this. Imagine you have a square block, 400px to a side. That&amp;rsquo;ll look fine on your screen, not taking up a huge amount of room - but to your physics engine, that&amp;rsquo;s 160 square &lt;em&gt;kilometres&lt;/em&gt; to deal with. You&amp;rsquo;re moving around an object the size of &lt;em&gt;Liechtenstein&lt;/em&gt;. Your viewport is therefore going to be comparatively enormous just to fit it on screen, and because of how the orthographic perspective works, it&amp;rsquo;ll assume it&amp;rsquo;s very far away - meaning a lot of your physical simulations, particularly those involving gravity, are going to appear very slow indeed. Unless you scale all your forces up to compensate, but surely that&amp;rsquo;s &lt;em&gt;more&lt;/em&gt; work?&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve selected 128px/m - it leaves me with a neat 10m width viewport, perfect for the sort of game I&amp;rsquo;m making, and is also a power of two, meaning it might be helpful with memory management of textures - depending on implementation and OpenGL version. Now that we&amp;rsquo;ve got this, we can build a little &amp;lsquo;Scaling&amp;rsquo; class to handle the translation for us. Here&amp;rsquo;s mine.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Scaling&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TARGET_WIDTH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;1280&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TARGET_HEIGHT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;720&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PIXELS_PER_METER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;128&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DEFAULT_TILE_WIDTH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;64&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;pixelsToMeters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pixels&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pixels&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PIXELS_PER_METER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;metersToPixels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;meters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;meters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PIXELS_PER_METER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getInitialWidth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pixelsToMeters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TARGET_WIDTH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//The initial width in metres.&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getInitialHeight&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pixelsToMeters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TARGET_HEIGHT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//As above, for height.&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;You might not need all these methods. Note that the three integers declared at the top are marked &lt;strong&gt;final&lt;/strong&gt;, but the float &lt;em&gt;PIXELS_PER_METER&lt;/em&gt; is not - that&amp;rsquo;s because we&amp;rsquo;ll be modifying this as our viewport changes size.&lt;/p&gt;&#10;&lt;p&gt;Next, we need to define what happens when the screen resizes. LibGDX has a method for this that you can override in the appropriate place. Mine looks like this (I&amp;rsquo;m also calling it as part of my initial setup).&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Scaling&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PIXELS_PER_METER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Scaling&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PIXELS_PER_METER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Scaling&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PIXELS_PER_METER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Scaling&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PIXELS_PER_METER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setToOrtho&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;p&gt;The variables &lt;em&gt;h&lt;/em&gt; and &lt;em&gt;w&lt;/em&gt; are fields inside my &lt;em&gt;Screen&lt;/em&gt; object - they keep track of the size of the viewport, in metres in this case. There&amp;rsquo;s not much point keeping track of the size in pixels, as we can just do &lt;strong&gt;Gdx.graphics.getWidth()&lt;/strong&gt;, and so on. Here&amp;rsquo;s what this code&amp;rsquo;s doing:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;If the new height is less than the new width (i.e., it&amp;rsquo;s a landscape aspect ratio):&#10;&lt;ul&gt;&#10;&lt;li&gt;Set the new px/m ratio to be the new width in pixels, divided by the current width in metres.&lt;/li&gt;&#10;&lt;li&gt;Scale the height according to the new scaling ratio.&lt;/li&gt;&#10;&lt;li&gt;Our width in metres remains the same!&lt;/li&gt;&#10;&lt;li&gt;Note: if you&amp;rsquo;re stretching the viewport instead, you&amp;rsquo;ll want a different px/m ratio for each axis.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;If the screen has a portrait aspect ratio, do the opposite.&lt;/li&gt;&#10;&lt;li&gt;Set our viewport, &lt;em&gt;camera&lt;/em&gt;, to have the new dimensions.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;One of our screen dimensions in metres will stay the same - this means objects stay a reasonable size on the screen, and making the viewport larger doesn&amp;rsquo;t simply equal &amp;lsquo;zooming out&amp;rsquo;. Note that we set our camera&amp;rsquo;s dimensions using metres rather than pixels - this is fine! LibGDX doesn&amp;rsquo;t care, it&amp;rsquo;ll draw everything at the appropriate size on screen, provided the dimensions you give to your rendering and drawing methods - &lt;em&gt;SpriteBatch&lt;/em&gt; and the like - are in metres too.&lt;/p&gt;&#10;&lt;p&gt;Now that we&amp;rsquo;ve done this, we should be able to resize our windows so that our game is still playable(ish), even at frankly silly aspect ratios.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2014/09/dinoratio.png" alt=""&gt;{: .center-image}&lt;/p&gt;&#10;&lt;p&gt;Cool, huh? Hopefully it should be obvious enough how to adjust this effect. You may have noticed that the drawing isn&amp;rsquo;t quite pixel-perfect, particularly on the &amp;lsquo;Dino-Bar&amp;rsquo;, due to the scaling. A fix for this would be to have several different sizes of asset available, and pick the closest one according to what size it&amp;rsquo;s being drawn on-screen - we could use the &lt;em&gt;Scaling&lt;/em&gt; method &lt;em&gt;metersToPixels&lt;/em&gt; for this.&lt;/p&gt;&#10;&lt;p&gt;I someone finds this article useful - if you have any questions, please don&amp;rsquo;t hesitate to leave a comment below. Ta ta for now - and remember, pixels are the devil.&lt;/p&gt;</content:encoded></item><item><title>Review: FIFA 15</title><link>https://www.joelotter.com/review-fifa-15/</link><pubDate>Mon, 29 Sep 2014 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/review-fifa-15/</guid><description>&lt;p&gt;Anyone who knows me will know that I&amp;rsquo;m not really a huge fan of the beautiful game. I am, however, quite into &lt;em&gt;gaming&lt;/em&gt;, and so thought that it was only fair this latest upgrade get a review. I&amp;rsquo;ll look at a few different sections. Without further ado&amp;hellip;&lt;/p&gt;</description><content:encoded>&lt;p&gt;Anyone who knows me will know that I&amp;rsquo;m not really a huge fan of the beautiful game. I am, however, quite into &lt;em&gt;gaming&lt;/em&gt;, and so thought that it was only fair this latest upgrade get a review. I&amp;rsquo;ll look at a few different sections. Without further ado&amp;hellip;&lt;/p&gt;&#10;&lt;h3 id="new-features"&gt;New features&lt;/h3&gt;&#10;&lt;p&gt;As ever, FIFA&amp;rsquo;s changes have been incremental, despite some no doubt wishing for a significant shaking-up. Perhaps one of the more notable additions this year would be the £1.04 million of &lt;a href="http://www.bbc.co.uk/sport/0/football/29387309"&gt;watches&lt;/a&gt; handed out, many of them to FIFA executives, by the Brazilian Football Confederation after this year&amp;rsquo;s world cup. I&amp;rsquo;ve yet to see any positive effect on the actual game from this, but maybe things will get fixed in a patch. Or DLC, at worst. Let&amp;rsquo;s hope it costs less than £16k.&lt;/p&gt;&#10;&lt;p&gt;There was also supposed to be a bug fix for some &amp;lsquo;corruption&amp;rsquo; issues that have been happening (presumably in save files). Unfortunately, it looks like this patch &lt;a href="http://www.nytimes.com/2014/09/27/sports/soccer/fifas-sepp-blatter-says-corruption-report-will-remain-secret.html"&gt;won&amp;rsquo;t be going public&lt;/a&gt;. Fingers crossed it gets rolled out in some kind of closed beta, for now.&lt;/p&gt;&#10;&lt;h3 id="graphics"&gt;Graphics&lt;/h3&gt;&#10;&lt;p&gt;FIFA tries to look better every year, but is often marred by the same visual mis-steps. Some of the lighting effects are particularly impressive, such as in the executive boardroom:&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2014/09/fifa-boardroom.jpg" alt=""&gt;&lt;/p&gt;&#10;&lt;p&gt;Regrettably, however, some of the textures really are still rather horrible, with no real change in recent years.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2014/09/sepp.jpg" alt=""&gt;{: .center-image }&lt;/p&gt;&#10;&lt;h3 id="audio"&gt;Audio&lt;/h3&gt;&#10;&lt;p&gt;FIFA is often praised for its up-to-the-minute and well-selected soundtracks. I&amp;rsquo;m not entirely sure why - judging purely from what I can hear from Craven Cottage when I leave my windows open on a Saturday, it&amp;rsquo;s largely a sort of incoherent roaring. Perhaps adopting this as an official anthem would attract more music to the game.&lt;/p&gt;&#10;&lt;center&gt;&lt;iframe width="640" height="360" src="https://www.youtube.com/embed/45NOP1OA-EQ" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;/center&gt;&#10;&lt;h3 id="multiplayer"&gt;Multiplayer&lt;/h3&gt;&#10;&lt;p&gt;FIFA&amp;rsquo;s multiplayer remains as popular as ever, whether played on the same TV or online. Matches these days can even support up to 22 individual players on the pitch at once (not counting the refereeing team, etcetera).&lt;/p&gt;&#10;&lt;p&gt;This year, of course, brought a huge addition to FIFA&amp;rsquo;s multiplayer in the shape of the World Cup in Brazil. Unfortunately, this DLC that was totally unavoidable, retailing at a cost of about $14 billion - more than twice as much as the last time it was released. Of course, you could wait until the prices drop, but by that time nobody will be playing anymore. That, and Brazil&amp;rsquo;s government will be wondering what&amp;rsquo;s happened to their education budget.&lt;/p&gt;&#10;&lt;p&gt;There were concerns over some hacking incidents on the World Cup servers, such as &lt;a href="http://www.bbc.co.uk/news/world-latin-america-18017540"&gt;forcing stadiums to sell alcohol&lt;/a&gt;, despite an earlier bugfix being implemented after some violence errors. Hopefully this has now been patched.&lt;/p&gt;&#10;&lt;h3 id="controls"&gt;Controls&lt;/h3&gt;&#10;&lt;p&gt;Nobody appears to have any control over what FIFA does.&lt;/p&gt;&#10;&lt;h3 id="conclusion"&gt;Conclusion&lt;/h3&gt;&#10;&lt;p&gt;What did you expect? It&amp;rsquo;s the same. Every year - same game, same corrupt organisation. Yawn. My copy of FIFA &amp;lsquo;04 will do fine. In the meantime, I&amp;rsquo;m going back to playing To The Moon. Toodles.&lt;/p&gt;</content:encoded></item><item><title>Review: 'Shame' by John Berkavitch</title><link>https://www.joelotter.com/review-shame-by-john-berkavitch/</link><pubDate>Sat, 10 May 2014 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/review-shame-by-john-berkavitch/</guid><description>&lt;p&gt;&lt;em&gt;&amp;ldquo;Put your hands in the air. Now put them down if you&amp;rsquo;ve never done anything you regret.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;</description><content:encoded>&lt;p&gt;&lt;em&gt;&amp;ldquo;Put your hands in the air. Now put them down if you&amp;rsquo;ve never done anything you regret.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;As every hand in the room remains aloft, John Berkavitch is joined onstage by three men holding umbrellas.&lt;/em&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;em&gt;&amp;ldquo;Don&amp;rsquo;t worry about them,&amp;rdquo; he says, &amp;ldquo;They&amp;rsquo;re just my imagination.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;&#10;&lt;p&gt;So begins Shame, a show combining poetry, storytelling, movement, music and clever use of projection to create something truly unique. The tagline of the show is &amp;ldquo;What is the worst thing you&amp;rsquo;ve ever done?&amp;rdquo;, and in seventy minutes Berkavitch relates five different events, from his early childhood to his mid-twenties.&lt;/p&gt;&#10;&lt;p&gt;The narrative is complex without being convoluted, with the five different plots being interwoven in a non-linear fashion. Although incredibly raw and personal throughout, it still manages to be full of John&amp;rsquo;s characteristic wit, and these five stories are all curiously uplifting. His words feel meticulously crafted, shifting in and out of different rhymes and rhythms with seeming effortlessness, creating intensely vivid imagery and a feeling of movement - and that&amp;rsquo;s before the dancers have even started doing their thing.&lt;/p&gt;&#10;&lt;p&gt;Shame is a technical marvel. Berkavitch is joined by three pioneering break dancers, who provide a dynamism to the stories as well as acting as John&amp;rsquo;s stage props. I don&amp;rsquo;t want to spoil too much - however, one particularly stand-out moment was the three together playing the part of a coffee machine. This had John treating the front row like the queue in the coffee shop he was working in, asking for drinks orders and chatting about Christmas shopping. I&amp;rsquo;m not normally a massive fan of audience participation, but this was done so fluidly, and tied in so well to the plot, that it was nearly impossible not to buy into it. Suffice to say, I have never been so grateful to be handed an invisible toastie that&amp;rsquo;s just been pulled out of someone&amp;rsquo;s bum. Honestly.&lt;/p&gt;&#10;&lt;p&gt;Not content to let these three do all the hard work, Berkavitch is just as involved with the movements; frequently diving between and over limbs, being lifted and carried, being restrained by the others and many more besides. The result is stunning - as the movement is all in relation to the story being told, it never feels disjointed or detracts from John&amp;rsquo;s words.&lt;/p&gt;&#10;&lt;p&gt;The stage itself is minimalistic - a white floor and a white backdrop. It&amp;rsquo;s the addition of several projectors - at least five, I think - that allow images to be placed on the floor, the wall and even on the bodies of the actors. This is used in some incredibly inventive ways, while, again, never feeling out of place or providing unnecessary distraction. I was surprised (and amazed) to see that the lighting and music are all controlled from a laptop off to the side. This is started at the beginning of the show and then never again touched - the whole seventy minutes are choreographed with military precision. The sheer amount of practice that must have gone into this production is staggering.&lt;/p&gt;&#10;&lt;p&gt;The show ends with John asking the audience if anyone has a story they&amp;rsquo;d like to share. This feels rather sudden - though I suppose we should have expected it, especially given something similar was mentioned at the beginning. Unfortunately nobody did, though I&amp;rsquo;d be interested to know if this is the norm or if we were just a particularly shy crowd.&lt;/p&gt;&#10;&lt;p&gt;Shame pulls together elements from across the artistic spectrum into one incredibly polished production, which is an achievement in itself. However, it&amp;rsquo;s the inventiveness of the movement and animation, the unwavering attention to detail and, of course, John Berkavitch&amp;rsquo;s incredible talent for storytelling that really make it stand out. I fully intend to go and see it again - you should come too.&lt;/p&gt;&#10;&lt;center&gt;&lt;iframe width="640" height="360" src="https://www.youtube.com/embed/Q6oYyqP3bSE" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;/center&gt;&#10;&lt;p&gt;&lt;em&gt;I saw Shame at the Roundhouse in Camden. It&amp;rsquo;s nearing the end of its current tour - there&amp;rsquo;s more information on John Berkavitch&amp;rsquo;s &lt;a title="Berkavitch" href="http://www.berkavitch.com/"&gt;website&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title>Uplift: A Postmortem</title><link>https://www.joelotter.com/uplift-a-post-mortem/</link><pubDate>Mon, 21 Apr 2014 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/uplift-a-post-mortem/</guid><description>&lt;p&gt;&lt;em&gt;Before I begin, I should point out: Uplift isn&amp;rsquo;t dead. The Kickstarter for Uplift is dead. Sensationalist title, I know. Let&amp;rsquo;s pretend I&amp;rsquo;ve been learning how to be a journalist. Because having a blog&lt;/em&gt; definitely &lt;em&gt;makes me a journalist, right? Ahem.&lt;/em&gt;&lt;/p&gt;</description><content:encoded>&lt;p&gt;&lt;em&gt;Before I begin, I should point out: Uplift isn&amp;rsquo;t dead. The Kickstarter for Uplift is dead. Sensationalist title, I know. Let&amp;rsquo;s pretend I&amp;rsquo;ve been learning how to be a journalist. Because having a blog&lt;/em&gt; definitely &lt;em&gt;makes me a journalist, right? Ahem.&lt;/em&gt;&lt;/p&gt;&#10;&lt;p&gt;Bearwaves launched a &lt;a href="https://www.kickstarter.com/projects/joelotter/uplift-a-multi-platform-rpg"&gt;Kickstarter&lt;/a&gt; for our game, Uplift, on the 20th March, and we reached the deadline a couple of days ago, having raised £961 from 77 pledges. This is by no means a small amount of money or number of backers, and we&amp;rsquo;re actually really pleased with what we accomplished, and immensely grateful to everyone who supported us. However, it was a long way off our £8,000 goal. In this post I&amp;rsquo;m going to attempt to learn as much as I can from this failure, in the hope that it&amp;rsquo;ll help us as we move on with Uplift, and that it might prove of some help to any developers in the planning stages for their own Kickstarter project.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ll include some of my own observations, as well as some of the feedback we received from places like HackerNews. Some of this feedback was quite harsh, but I feel it&amp;rsquo;s all valid and important to internalise; this isn&amp;rsquo;t intended as any kind of pity-seeking operation.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ll focus first on the problems with the Kickstarter - lessons learned in hindsight. These aren&amp;rsquo;t necessarily useful to us now, as we&amp;rsquo;re unlikely to use crowdfunding again (at least for a long while). They may, however, be helpful for anyone looking for pitfalls in their own project - perhaps, if your own pitch has some of the same shortcomings ours did, you may be able to avoid making our mistakes. I&amp;rsquo;ll then focus on Uplift itself, and what changes we&amp;rsquo;re going to make moving forward. Here we go.&lt;/p&gt;&#10;&lt;h3 id="the-video"&gt;The Video&lt;/h3&gt;&#10;&lt;p&gt;The video is the first thing you see when you land on a Kickstarter page, so it makes sense to analyse this first of all.&lt;/p&gt;&#10;&lt;p&gt;Our video wasn&amp;rsquo;t good enough. This much is obvious, and to be totally honest I think we knew this - that&amp;rsquo;s why we put so much effort into filling the text part with content. I could talk for ages about the poor lighting, shoddy cameras and dodgy shooting techniques that were used, but I&amp;rsquo;m not going to - we aren&amp;rsquo;t filmmakers, and we don&amp;rsquo;t have good equipment. I think a well-shot video is certainly very helpful, but it&amp;rsquo;s not going to dictate whether or not your hit your funding goal; thus, for now, I&amp;rsquo;ll only talk about the things we were capable of changing in ours.&lt;/p&gt;&#10;&lt;p&gt;The first and probably most important point to make is the order in which the footage was shown. Starting the video with &amp;rsquo;talking heads&amp;rsquo; - us introducing the game - is all very well and gives the project a more human feel. However, we should almost certainly have played the footage of the game much closer to the beginning. The video had just over 3,700 plays, with around 7% of viewers watching it the whole way through - this means there were very likely a few hundred people who lost interest in the video before seeing any footage of the game, or skipped past what we were saying to get to it. Explaining your project is great, but with a video game people want to see a video of the game - we didn&amp;rsquo;t deliver on this well enough.&lt;/p&gt;&#10;&lt;p&gt;Another issue was the game footage itself. I&amp;rsquo;m not going to comment here on how the game looks - that comes later - but rather what bits of it we chose to show. We&amp;rsquo;re billing Uplift as a game with:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Exploration&lt;/li&gt;&#10;&lt;li&gt;A &amp;lsquo;proper&amp;rsquo; storyline&lt;/li&gt;&#10;&lt;li&gt;RPG elements&lt;/li&gt;&#10;&lt;li&gt;Puzzles&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;We showed a little bit of the space flight, and some footage of the player walking around. That was pretty much it - we therefore demonstrated a tiny amount of exploration, and none of the other three points. Annoyingly, in hindsight, NPC interaction and the quest log are some things we&amp;rsquo;ve already built into the game, but weren&amp;rsquo;t included in the footage as we didn&amp;rsquo;t think they would look good or work well in a video. That was another mistake - when you show footage of a role-playing game, potential customers want to see some role-playing.&lt;/p&gt;&#10;&lt;p&gt;Another piece of feedback given to us was that we didn&amp;rsquo;t seem enthusiastic in the video. I&amp;rsquo;m not quite sure how to take this one - granted, we weren&amp;rsquo;t quite ourselves as none of us are used to talking to cameras, but I don&amp;rsquo;t think we were &lt;em&gt;excessively&lt;/em&gt; wooden. I suppose (to make a sweeping generalisation) we Northern Irish can seem a bit more subdued; I guess this one&amp;rsquo;s more dependant on who you are and who your audience is.&lt;/p&gt;&#10;&lt;h3 id="the-game-was-too-incomplete"&gt;The game was too incomplete&lt;/h3&gt;&#10;&lt;p&gt;This one seems a little counterintuitive - surely Kickstarter is for uncompleted projects? The rules on the platform state that for a project to be given the go-ahead you need some kind of proof that you&amp;rsquo;re both working on it &lt;em&gt;and&lt;/em&gt; capable of finishing it. We met the requirements of Kickstarter itself, but not those of our potential backers.&lt;/p&gt;&#10;&lt;p&gt;We&amp;rsquo;ve been working on Uplift since May 2012. That seems like a long time, but in reality &amp;lsquo;working on&amp;rsquo; has meant &amp;lsquo;sporadically committing code and assets whenever we get the chance, along with totally rewriting the game from scratch after a year&amp;rsquo;. To us, of course, this makes perfect sense - we&amp;rsquo;re students, we&amp;rsquo;re very busy, we don&amp;rsquo;t have a budget and we just don&amp;rsquo;t have &lt;em&gt;time&lt;/em&gt; to work consistently on the game. Indeed, that&amp;rsquo;s what the purpose of the Kickstarter was. To a potential backer, though, unless reading very carefully, this merely makes us look lazy and/or incompetent. We know we&amp;rsquo;re capable of creating this game, given some free time - after all, we know our own skillsets - but we didn&amp;rsquo;t give enough proof of this to potential backers.&lt;/p&gt;&#10;&lt;p&gt;It certainly didn&amp;rsquo;t help that a lot of the work done so far on Uplift has gone into the game&amp;rsquo;s backend - we have a fairly comprehensive system in place for level designing, building quests and creating NPCs with their associated dialogue. This means that actually building levels and characters into the game can largely be done without writing any code - great for us, but the result is that we simply didn&amp;rsquo;t have much user-facing stuff we could show off; ideally we should have had some kind of playable demo. We did have our old Android build, but it&amp;rsquo;s rather outdated, and if we&amp;rsquo;re marketing a multi-platform game, there should be a multi-platform demo to go along with it. Some more screenshots and footage would have not gone amiss either.&lt;/p&gt;&#10;&lt;h3 id="the-pre-launch"&gt;The pre-launch&lt;/h3&gt;&#10;&lt;p&gt;One piece of feedback we were given, that we hadn&amp;rsquo;t quite taken into consideration before, was that we didn&amp;rsquo;t do enough to promote the Kickstarter project before its launch. We had a Facebook page with a couple of hundred &amp;rsquo;likes&amp;rsquo;, and we posted it there, but the majority of people who had liked the page were friends of ours and so would have seen it anyway. Building some kind of community before launch isn&amp;rsquo;t necessarily essential - I&amp;rsquo;m sure there are many projects which have built their community post-launch - but it can&amp;rsquo;t hurt.&lt;/p&gt;&#10;&lt;p&gt;There are, of course, several other things we could have done better on the Kickstarter. Perhaps a better justification of why people would &lt;em&gt;want&lt;/em&gt; the game, or what&amp;rsquo;s special about it. Perhaps we should have got a proper artist for those sketches instead of just getting a mate who likes to draw to do them. At the end of the day, the Kickstarter was amateurish because we are amateurs - which is fine, but it doesn&amp;rsquo;t necessarily mean people will want to give us their hard-earned cash.&lt;/p&gt;&#10;&lt;p&gt;With the feedback we got, we&amp;rsquo;ve learned of a few changes we could potentially make to Uplift itself as we move forward.&lt;/p&gt;&#10;&lt;h3 id="the-graphics"&gt;The graphics&lt;/h3&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2014/04/uplift-pc.png" alt=""&gt;{: .center-image}&lt;/p&gt;&#10;&lt;p&gt;Uplift&amp;rsquo;s graphics are kind of sort of not very good. There&amp;rsquo;s no point um-ing and ah-ing around this issue; it needs to be addressed.&lt;/p&gt;&#10;&lt;p&gt;Personally, I quite like the very simplistic, pixelated style of the game. It keeps our filesize down, and requires a very small amount of memory at runtime, meaning the game performs well on even old hardware. It&amp;rsquo;s not ugly, by any stretch of the imagination. However, it&amp;rsquo;s very easy to see that the game simply doesn&amp;rsquo;t look good enough to be a modern PC game (even an &amp;lsquo;indie&amp;rsquo; game). This isn&amp;rsquo;t the fault of Matthew, who is a coder by trade anyway, but rather is the fault of our very slow development rate.&lt;/p&gt;&#10;&lt;p&gt;When we started making Uplift, it was to be Android-only, and our reference device (&amp;ldquo;it should run well on this as a minimum&amp;rdquo;) was the original HTC Desire, which had a 1GHz, single-core CPU - not quite completely redundant at the time, but absolutely feeble nowadays. We even took steps to make sure the game would run on the HTC &lt;em&gt;Wildfire&lt;/em&gt; - a phone that simply no-one would even attempt to play games on any more. Our simple graphical style was perfect then, but now, two years later, we&amp;rsquo;re targeting devices with multiple cores, double the clock speed and well over twice the screen resolution - and that&amp;rsquo;s just the mobile version. If our game is going to work at all on PC, or even modern mobile devices, the graphics need to be totally revamped, whether that&amp;rsquo;s with a higher level of detail or a different style entirely.&lt;/p&gt;&#10;&lt;p&gt;This presents us with a problem - Matthew is a pretty good artist, but it&amp;rsquo;s not his primary focus, and will likely be too busy to contribute much once he graduates. We&amp;rsquo;re waiting to see what he comes up with as a concept for a redesign, but if you&amp;rsquo;re interested, we are looking for graphics artists. &lt;a href="http://www.facebook.com/Bearwaves"&gt;Get in touch&lt;/a&gt;!&lt;/p&gt;&#10;&lt;h3 id="the-game-is-too-static"&gt;The game is too static&lt;/h3&gt;&#10;&lt;p&gt;When you explore Uplift&amp;rsquo;s environments, they&amp;rsquo;re not terribly dynamic or immersive. We&amp;rsquo;ve got some cool particle effects on the stars in space, and NPCs do turn around to speak to you, but along with the enemies that&amp;rsquo;s pretty much it. We had planned to add a bit more life to the environments anyway, but the feedback has shown us just how important this is. The game can look great and have a great story, but if it doesn&amp;rsquo;t &lt;em&gt;feel&lt;/em&gt; great or play well it won&amp;rsquo;t be a good game. Uplift is supposed to be immersive in terms of a great plot and big universe, but if that universe feels devoid of life it won&amp;rsquo;t be immersive in the slightest.&lt;/p&gt;&#10;&lt;h3 id="so-whats-next"&gt;So what&amp;rsquo;s next?&lt;/h3&gt;&#10;&lt;p&gt;Uplift&amp;rsquo;s not going away. This Kickstarter has just made us even more keen to get back to work on the game, and now that we&amp;rsquo;re not tied down with how much funding we receive, we can add more people to the team. Development &lt;em&gt;will&lt;/em&gt; be slow, given that most of us are working through the summer, but there should be time to get quite a bit done. We&amp;rsquo;ll try to communicate as much as possible, so that everyone can track development as it progresses.&lt;/p&gt;&#10;&lt;p&gt;If you&amp;rsquo;re about to embark on your own Kickstarter adventure, I hope you&amp;rsquo;ve found this at least a little bit useful - and from all of us here at Bearwaves, good luck! Even if you don&amp;rsquo;t succeed, it&amp;rsquo;s a massively valuable experience and actually pretty fun. Do let us know how it goes!&lt;/p&gt;&#10;&lt;p&gt;If you&amp;rsquo;re interested in Uplift, I hope this has satisfied your curiosity a bit as to what&amp;rsquo;s going to happen to the project. Keep an eye on this blog or our &lt;a href="http://www.facebook.com/Bearwaves"&gt;Facebook page&lt;/a&gt; - there&amp;rsquo;ll be more news along soon!&lt;/p&gt;</content:encoded></item><item><title>Review: FEZ (PC)</title><link>https://www.joelotter.com/review-fez-pc/</link><pubDate>Sun, 19 May 2013 00:00:00 +0000</pubDate><guid>https://www.joelotter.com/review-fez-pc/</guid><description>&lt;p&gt;Polytron&amp;rsquo;s XBLA hit (and personal favourite of Mojang&amp;rsquo;s Notch) has finally made in onto PC in the last couple of weeks. I downloaded the game yesterday and finished it this morning, after a small delay - I saw the sunrise creeping at my window and decided I&amp;rsquo;d better get some sleep. FEZ is absolutely fantastic.&lt;/p&gt;</description><content:encoded>&lt;p&gt;Polytron&amp;rsquo;s XBLA hit (and personal favourite of Mojang&amp;rsquo;s Notch) has finally made in onto PC in the last couple of weeks. I downloaded the game yesterday and finished it this morning, after a small delay - I saw the sunrise creeping at my window and decided I&amp;rsquo;d better get some sleep. FEZ is absolutely fantastic.&lt;/p&gt;&#10;&lt;p&gt;The game begins with Gomez (the rectangular-headed chap who you control) waking up in his room. He exits into the village, the beautifully designed, two-dimensional platforming environment you can see in the screenshot above. The characters have very little to say; they&amp;rsquo;re quite two-dimensional - a clever twist on the genre by Polytron, and one that is, along with the game&amp;rsquo;s numerous other jokes and references, really funny.&lt;/p&gt;&#10;&lt;p&gt;The idea of &amp;lsquo;dimension&amp;rsquo; is what really makes this game tick, as you see very near to the start of the game. Gomez is given a fez (the little red hat the game is named after), which allows him to rotate the world. I played with a controller, so for me pulling the left or right triggers or bumpers rotated the whole level by 90 degrees.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2013/05/fez4.jpg" alt=""&gt;{: .center-image}&lt;/p&gt;&#10;&lt;p class="caption"&gt;This 4D-cube-thing is used as the loading screen for the bigger areas.&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s not simply a 3D environment viewed from different angles, though - for example, two platforms that are level with each other but at different distances from the camera. Since distance means nothing in 2D, the player can simply walk from one to the other - provided you&amp;rsquo;ve rotated the world correctly, of course. FEZ twists space in an echo of how Braid twisted time, and Jon Blow is actually given special thanks in the credits. These two games compliment each other magnificently. Thanks is also given to Superbrothers, a game which shares FEZ&amp;rsquo;s artistic heritage.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2013/05/fez3.jpg" alt=""&gt;{: .center-image}&lt;/p&gt;&#10;&lt;p class="caption"&gt;This ancient city is actually eerie as hell.&lt;/p&gt;&#10;&lt;p&gt;The game relies on doors to traverse the various levels and worlds. The world map can be tricky to get the hang of, but it&amp;rsquo;s absolutely perfect for the game it services. Some worlds will have cube bits (the collectable you&amp;rsquo;re after in the game), multiple doors, treasure chests, whole cubes, warp gates or &amp;ldquo;secrets&amp;rdquo; - often a devilishly tricky puzzle, the solving of which will reward the player with a rarer version of the cube called an anticube.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2013/05/fez1.jpg" alt=""&gt;{: .center-image}&lt;/p&gt;&#10;&lt;p class="caption"&gt;There's a huge amount of variety in the level design. FEZ is gorgeous.&lt;/p&gt;&#10;&lt;p&gt;The goal of the game is to gather all of the collectables - there are no boss battles, enemies to fight, quests or goals. FEZ encourages players to explore at their own pace, and backtracking will often result in finding a previously unopened door, which will lead to a whole string of new worlds. Getting the 32 cubes required to &amp;lsquo;beat&amp;rsquo; the game isn&amp;rsquo;t terribly difficult (anticubes count too), but the real challenge lies in finding absolutely everything. FEZ is &lt;em&gt;not&lt;/em&gt; an easy game. Some of the puzzles had me really frustrated, in a good way, whether it be dimension hopping, decoding or shifting blocks around.&lt;/p&gt;&#10;&lt;p&gt;FEZ&amp;rsquo;s world has &lt;em&gt;two&lt;/em&gt; codes. One is the &amp;lsquo;cube&amp;rsquo; language, spoken by the cube at the start of the game. I won&amp;rsquo;t reveal the other one as it would give too much away. FEZ doesn&amp;rsquo;t offer translation for either code - you&amp;rsquo;ve got to work it out for yourself, and many of the puzzles will rely on one or the other. Without spoiling anything, the way FEZ subtly shows players how to read the &amp;lsquo;cube&amp;rsquo; language is an absolute work of genius.&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://www.joelotter.com/img/2013/05/fez2.jpg" alt=""&gt;{: .center-image}&lt;/p&gt;&#10;&lt;p class="caption"&gt;I know what this says. I'm not telling, though.&lt;/p&gt;&#10;&lt;p&gt;The nature of these puzzles meant that I played most of the game with a pen and paper beside me, something which I absolutely love and is sadly missing from game design these days. FEZ&amp;rsquo;s world-hopping and puzzles made me nostalgic for the Myst games, but its play mechanic blows just about everything out of the water.&lt;/p&gt;&#10;&lt;p&gt;FEZ is a game I waited a long, long time to play. I&amp;rsquo;m slightly sad I beat the &amp;lsquo;main game&amp;rsquo; in two days, but it&amp;rsquo;s far from over - I&amp;rsquo;ve got 32 more cubes to find. It&amp;rsquo;s £6 on Steam - you&amp;rsquo;d be mad not to give it a go.&lt;/p&gt;</content:encoded></item></channel></rss>