<?xml version="1.0" encoding="UTF-8"?>

<feed xmlns="http://www.w3.org/2005/Atom">
<link href="https://www.swilliams.io/atom.xml" rel="self" type="application/atom+xml"/>
<link href="https://www.swilliams.io" rel="alternate" type="text/html"/>
<title type="html">SWilliams.io</title>
<id>https://www.swilliams.io</id>
<author>
    <name>Scott Alexander Williams</name>
    <email>woohoowilliams@gmail.com</email>
</author>
<entry>
<title>Note-taking in Obsidian</title>
<link href="https://www.swilliams.io/w/notetaking-in-obsidian" />
<id>https://www.swilliams.io/w/notetaking-in-obsidian/</id>
<updated>2026-01-13T01:01:01Z</updated>
<content><![CDATA[<p>For the past few years, I've been using <a href="https://obsidian.md/">Obsidian</a> as my primary note-taking app. It's been pretty great so far for recording my thoughts and building a personal knowledge base.</p>
<p>Previously, I relied on OneNote during university, but eventually stopped due to general dissatisfaction with Microsoft. I switched to Notion for a few years, but frequent downtime and its always-online requirement meant that on three separate occasions I couldn't access my notes when I needed them. Obsidian strikes the right balance for me and provides what I need in a note-taking solution.</p>
<p>Obsidian has a local-first approach. Notes are stored locally as plain text Markdown files, which means they aren't reliant on a stable internet connection. Being plain text also means my notes will still be around even if Obsidian isn't. It is essentially a long term guarantee against vendor lock-in and product degradation.</p>
<p>The local-first nature, combined with Obsidian Sync which allows syncing across devices, means that Obsidian offers the reliability of a local-first app with the convenience of cloud syncing.</p>
<p>Despite using plain text Markdown files, Obsidian offers the key ability to link notes, creating a network of interconnected ideas. Combined with tags and robust search functionality, it's easy to navigate and rediscover old notes as needed.</p>
<p>It also generates a pretty graph.</p>
<figure>
    <img src="/w/notetaking-in-obsidian/obsidian.png" href="obsidian.png" alt="Graph of notes in Obsidian.">
    <figcaption>Obligatory Obsidian graph. Every node is a note and every line is a link.</figcaption>
</figure>
<p>Obsidian also has a rich plugin ecosystem, though I would caution against over-reliance on plugins, as you lose the simplicity of the core experience. The one plugin I really do recommend is <a href="https://github.com/SebastianMC/obsidian-custom-sort">Custom Sort</a> which allows you to specify individually how each folder is to be sorted, e.g.: one folder could be sorted by note name while another is sorted by created date.</p>
<p>My note-taking strategy is straightforward: I create one daily note per day, keeping the structure flexible, and using links to connect related ideas. My daily notes focus on projects and people I interact with.</p>
<p>I use tags and folders to categorise notes broadly, limiting the number of tags to keep things manageable and using a relatively flat folder structure to enable quickly finding the right note.</p>
<p>I've moved my internet bookmarks into Obsidian notes, which prevents link rot, allows searching, and enables me to link related resources. I've never again had a moment where I couldn't recover a useful blog post or website bookmark from vague memories.</p>
<p>I've also started writing notes on books I'm reading, capturing key ideas and quotes that resonate with me. And I also store recipes in Obsidian, making it easy to find and reference them when I need to cook.</p>
<p>Obsidian has become an invaluable tool I use every day as part of my second brain.</p>
]]></content>
</entry>
<entry>
<title>Fixing AWS Lightsail crashing after a few hours</title>
<link href="https://www.swilliams.io/w/fixing-lightsail-resets" />
<id>https://www.swilliams.io/w/fixing-lightsail-resets/</id>
<updated>2025-09-23T01:01:01Z</updated>
<content><![CDATA[<p>A persistent and annoying issue affects Amazon Web Services (AWS) Lightsail instances: they shut down seemingly randomly after only a few hours. The instance seems fine for an hour or two, then suddenly shuts itself down. This problem is very visible on the web, for example: on <a href="https://repost.aws/questions/QUMKlYvYYjQCOlHqts8Gkisw/wordpress-lightsail-instance-keeps-crashing-even-with-very-little-traffic">AWS rePost</a>, <a href="https://www.reddit.com/r/aws/comments/sx0ee9/lightsail_instance_downs_every_two_days/">Reddit</a>, and <a href="https://stackoverflow.com/questions/65937467/aws-lightsail-seems-to-crash-super-easily">StackOverflow</a>, though none offer a full solution. The Lightsail metrics offered give no clues as to what causes this - CPU usage and network traffic both look normal and well within limits. The issue is due to the memory utilisation, which Lightsail does not surface.</p>
<p>After the instance has crashed once more, reboot it, and take the following steps to diagnose the issue.</p>
<p>Check the logs of the instance by running the command <code class="inline">sudo less /var/log/syslog</code>, scrolling back to the time of the crash. Searching for anything that points to out of memory errors - look for "oom". In my case, the culprit was a PHP process /usr/lib/php/sessionclean, which was running on an hourly cron job and apparently had no limits to the amount of memory it could consume.</p>
<p>There are two solutions, and you should do both: tame the PHP sessionclean service; and increase the swap space available for memory usage.</p>
<p>Limiting the PHP sessionclean service can be done by running <code class="inline">sudo systemctl edit phpsessionclean.service</code> then limiting the maximum memory of the service to 100 Megabytes by adding:</p>
<code class="block">
    [Service]
    MemoryMax=100M
</code>
<p>Then restarting the service with.</p>
<code class="block">
    sudo systemctl daemon-reload
    sudo systemctl restart phpsessionclean.service
</code>
<p>Increasing the swap space can be done by creating a new swap file. Run the following commands to allocate 2GB to a swap file:</p>
<code class="block">
    sudo fallocate -l 2G /swapfile
    sudo chmod 600 /swapfile
    sudo mkswap /swapfile
    sudo swapon /swapfile
    echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
</code>
<p>Verify that the above commands worked by running <code class="inline">free -h</code>.</p>
<p>With these two changes in place, your Lightsail instance should stop crashing every few hours and hopefully run reliably, even on the smallest plans.</p>
]]></content>
</entry>
<entry>
<title>SQL Murder Mystery - Review</title>
<link href="https://www.swilliams.io/w/sql-murder-mystery-review" />
<id>https://www.swilliams.io/w/sql-murder-mystery-review/</id>
<updated>2025-08-29T01:01:01Z</updated>
<content><![CDATA[<p>Found at <a href="https://mystery.knightlab.com">mystery.knightlab.com</a>, the SQL Murder Mystery game is a fun little exercise in using SQL to solve a murder mystery. The game is good for both beginners and masters alike, since there is no required way to solve the mystery and there is no time or query limit preventing your exploration. I highly recommend anyone with any SQL skills to give it a go. The rest of this article will serve as to explain how I solved it. Spoilers ahead!</p>
<h2>Solution</h2>
<p>Your first step will be to investigate what tables you can explore. The game provides your first query to list all the tables available to you:</p>
<code class="block">
SELECT name 
FROM sqlite_master
WHERE type = 'table'
</code>
<p>The tables listed will include <code class="inline">crime_scene_report</code>, which the game prompts you to optionally explore the structure of. Since the game tells you that the murder happened in SQL City on January 15th 2018, that should inform you how to query the <code class="inline">crime_scene_report</code> table:</p>
<code class="block">
SELECT *
FROM crime_scene_report
WHERE type = 'murder'
AND city = 'SQL City'
AND date = '20180115'
</code>
<p>The result of the above query points you to the next steps of the investigation: finding the appropriate witness statements. We're told the first witness lives at the last house on Northwestern Dr, and the second witness is called Annabel and lives somewhere on Franklin Ave. Terrible police work by the crime scene investigator, but thankfully enough that we can find the first and second witnesses within the <code class="inline">person</code> table:</p>
<code class="block">
WITH first_witness AS (
    SELECT * 
    FROM person 
    WHERE address_street_name = 'Northwestern Dr' 
    ORDER BY address_number DESC 
    LIMIT 1
),
second_witness AS (
    SELECT * 
    FROM person 
    WHERE name LIKE 'Annabel%' 
    AND address_street_name = 'Franklin Ave'
)
SELECT * FROM first_witness
UNION
SELECT * FROM second_witness
</code>
<p>The next step is to look at the interviews of our two witnesses. A simple query using the <code class="inline">id</code>s of the witnesses found in the previous query would suffice but you could definitely <code class="inline">JOIN</code> it with the <code class="inline">interview</code> table to get more content in one query, up to you. Once you found the interviews, you'll be given a series of clues to find the killer from each witness. You can find a single individual who fits all the clues, as follows:</p>
<code class="block">
SELECT name
FROM person
WHERE license_id in (
    SELECT id
    FROM drivers_license
    WHERE plate_number LIKE '%H42W%'
)
INTERSECT
SELECT name
FROM get_fit_now_member
WHERE membership_status = 'gold'
</code>
<p>This gives you the single individual who fits all the clues, and is thus the killer. Entering the name into the solution checker will reveal that there was a mastermind behind the operation. Identifying the real villain can be done in two queries as the game suggests. Your first step is to find the interview with the killer and discover the clues to their master's identity, easily done by querying the <code class="inline">interview</code> table with the killer's <code class="inline">id</code>. The second step involves using all the clues from that interview. My approach is to create a subquery with a list of suspects from one query and intersecting it with another query with the suspects from the other clue, as follows:</p>
<code class="block">
WITH attendees AS (
SELECT person.name AS name, COUNT(*)
    FROM facebook_event_checkin
    JOIN person
    ON facebook_event_checkin.person_id = person.id
    WHERE event_name = 'SQL Symphony Concert'
    AND date >=20171200
    AND date <=20171231
    GROUP BY person_id
    HAVING count(*) = 3
)
SELECT name FROM attendees
INTERSECT
SELECT person.name
FROM person
JOIN drivers_license
ON person.license_id = drivers_license.id
WHERE height >= 65
AND height <=67
AND car_model = 'Model S'
AND hair_color = 'red'
</code>
<p>Entering the resulting name into the solution checker will give you the game's victory speech, proclaiming you the greatest SQL detective of all time. Huzzah!</p>
<p>I had good fun playing this game and solving the mystery. I recommend it to anyone with any level of knowledge of SQL, as you can solve the mystery with simple queries and brute force or you can construct elaborate queries to solve it creatively and complicatedly.</p>
]]></content>
</entry>
<entry>
<title>Do stand-ups by ticket, not by person</title>
<link href="https://www.swilliams.io/w/standups-by-person-or-by-ticket" />
<id>https://www.swilliams.io/w/standups-by-person-or-by-ticket/</id>
<updated>2025-05-05T01:01:01Z</updated>
<content><![CDATA[<p>Let's say you're in a software engineering team with daily stand-ups and a sprint board with tickets up. How should you run the daily stand-ups?</p>
<p>It may be tempting to default to the classic "round-the-room" approach: going from person to person, asking each what they worked on yesterday, what they're doing today, and if they have any blockers. But in reality, this often does not work.</p>
<p>Instead, stand-ups should focus on the work itself: the tickets on the board, not the individuals in the meeting. Rather than structuring the conversation around each person, structure it instead around the flow of work. Start by looking at the board, moving from right to left across your workflow columns. Focus the discussion on the progress of the work items, not open-ended personal updates.</p>
<p>Why does this matter? Because it keeps priorities front and center. It aligns the team's attention on what's closest to delivering value, rather than on a scattered set of individual perspectives. It also reduces the risk of conversations veering off into tangents that don't serve the team's needs or goals.</p>
<p>Too often have I been in stand-ups where the vague open ended nature of questions has led to long, detailed updates about every minor task someone did the day before, in far too much detail. The update drifts off topic into areas of conversation that are ultimately irrelevant to the team. By focusing on the tickets in play, you naturally keep the conversation tighter, more relevant, and action-oriented.</p>
<p>Say if your board has columns for tickets like: Open, In Progress, In Peer Review, Releasable, Done.</p>
<ol>
    <li>Start at the right: the Releasable column. Ask whoever owns those tickets whether they're ready to be released. Is anything blocking deployment?</li>
    <li>Move to Peer Review. For tickets waiting on review, check whether reviews have been completed. If not, prompt the assignee to request a review, or assign a reviewer on the spot.</li>
    <li>Finally look at the work in progress. Ask about blockers, progress, or whether the ticket is still on track.</li>
    <li>Optionally at the end, look at the open column and discuss any changing priorities for what should be started next.</li>
</ol>
<p>By following the flow of the board, you ensure that the stand-up is about moving work forward, not just providing a laundry list of activity. You emphasize the status of the team's shared goals rather than individual activities.</p>
<p>Naturally however, this whole blog post is only relevant if your team uses tickets and boards to prioritise work items. If not, another approach would be needed to maintain focus on whats important.</p>
]]></content>
</entry>
<entry>
<title>Optimising GraphQL queries with @include and @skip</title>
<link href="https://www.swilliams.io/w/graphql-includes" />
<id>https://www.swilliams.io/w/graphql-includes/</id>
<updated>2024-07-21T01:01:01Z</updated>
<content><![CDATA[<p>Imagine you have a complex multi-faceted GraphQL query:</p>
<code class="block">
query(id: String) {
    thing(id) {
        foo {
            ...
        }
        bar {
            ...
        }
    }
}
</code>
<p>In some GraphQL resolvers, this might be transformed into SQL queries on a database like so:</p>
<code class="block">
SELECT * FROM foo WHERE id = $id;
SELECT * FROM bar WHERE id = $id;
</code>
<p>This may be fine if we always need data from both foo and bar. But if we do not always need data from both sources, it would be more efficient to ask for only what we need. This is where the <code>@include</code> and <code>@skip</code> directives come in handy. The <code>@include</code> and <code>@skip</code> directives allow you to conditionally include or exclude parts of a query based on variables, so resolvers are not called unnecessarily and we only end up getting the data that we want.</p>
<p>The <code>@include</code> directive includes a field only if a specified condition is true, like so:</p>
<code class="block">
query($id: String, $includeFoo: Boolean!) {
    thing(id: $id) {
        foo @include(if: $includeFoo) {
            ...
        }
        bar {
            ...
        }
    }
}
</code>
<p>With this query, the <code>foo</code> field will only be fetched if the variable <code>$includeFoo</code> is set to <code>true</code>. This translates to the following SQL queries:</p>
<code class="block">
-- When $includeFoo is true:
SELECT * FROM foo WHERE id = $id;
SELECT * FROM bar WHERE id = $id;

-- When $includeFoo is false:
SELECT * FROM bar WHERE id = $id;
</code>
<p>The <code>@skip</code> directive works in the opposite way: it skips a field if a specified condition is true.</p>
]]></content>
</entry>
<entry>
<title>Reordering how endpoints appear in an OpenAPI specification without reordering the endpoints</title>
<link href="https://www.swilliams.io/w/reordering-openapi-endpoints" />
<id>https://www.swilliams.io/w/reordering-openapi-endpoints/</id>
<updated>2024-04-20T01:01:01Z</updated>
<content><![CDATA[<p>Say you have a Swagger or OpenAPI specification  like so:
<code class="block">
openapi: 3.0.3
info:
  title: foobar
  version: 1.0.0
paths:
  /foo:
    get:
      tags:
        - foo
      responses:
        '200':
          description: Success
  /bar:
    get:
      tags:
        - bar
      responses:
        '200':
          description: Success
</code>
<p>If you wanted to update your documentation so that <code class="inline">/bar</code> appeared first before <code class="inline">/foo</code>, the easy and obvious solution would be to just copy and paste the <code class="inline">/bar</code> section above <code class="inline">/foo</code> like so:</p>
<code class="block">
openapi: 3.0.3
info:
  title: foobar
  version: 1.0.0
paths:
  /bar:
    get:
      tags:
        - bar
      responses:
        '200':
          description: Success
  /foo:
    get:
      tags:
        - foo
      responses:
        '200':
          description: Success
</code>
<p>However, this might not be desirable, either because its a lot of copy and pasting or because the specification is being autogenerated and you can't change easily the order in which its put together.</p>
<p>But did you know that the order in which endpoints appear in the specification can also be determined by the order of the tags within the top level tags object? Thus, an alternative way to update the documentation would be like so:</p>
<code class="block">
openapi: 3.0.3
info:
  title: foobar
  version: 1.0.0
tags:
  - name: bar
  - name: foo
paths:
  /foo:
    get:
      tags:
        - foo
      responses:
        '200':
          description: Success
  /bar:
    get:
      tags:
        - bar
      responses:
        '200':
          description: Success
</code>
<p>In this case, <code class="inline">/bar</code> will the shown first before <code class="inline">/foo</code> even though it is the second path being declared. Pretty neat. You can play around with this more in the <a href="https://editor.swagger.io/">Swagger Editor</a>.</p>
]]></content>
</entry>
<entry>
<title>How to forcibly end any process that is using a port, on Unix, presented in Haiku form</title>
<link href="https://www.swilliams.io/w/end-process-of-port" />
<id>https://www.swilliams.io/w/end-process-of-port/</id>
<updated>2023-03-25T01:01:01Z</updated>
<content><![CDATA[<p><code class="inline">lsof -i</code></p>
<p><code class="inline">: 8081</code></p>
<p>then <code class="inline">kill [process pid]</code></p>
]]></content>
</entry>
<entry>
<title>Using ReactJS on itch.io</title>
<link href="https://www.swilliams.io/w/react-on-itch" />
<id>https://www.swilliams.io/w/react-on-itch/</id>
<updated>2022-06-12T01:01:01Z</updated>
<content><![CDATA[<p>Despite clearly not being a game engine, ReactJS has a lot of potential for being used to create simple HTML based games - in fact, one of the first use
    cases React introduces to you in its tutorials is using the React framework to create <a href="https://reactjs.org/tutorial/tutorial.html">Tic Tac Toe</a>.
</p>
<p>So say you want to build a game in ReactJS and upload it to <a href="https://itch.io/">itch.io</a>. If you started your project by running
    <code class="inline">npx create-react-app</code>, then ran <code class="inline">npm run build</code>, zipped up the folder, then uploaded it to itch.io, you
    will see a result of something like this:</p>
<figure>
    <img src="/w/react-on-itch/broke.png" href="/w/react-on-itch/broke.png" alt="An itch.io draft game page.">
    <figcaption>The game is just blank. Despite it working when you ran the React project locally, with <code class="inline">npm start</code>, it does not work
        on itch.io. </figcaption>
</figure>
<p>So how do you fix this? The solution is, thankfully, quite simple. Go into your <code class="inline">package.json</code> and add the following line after the
    version number:</p>
<code class="block">"homepage": ".",</code>
<p>Then simply rebuild the project, upload it, and the results should be as follows:</p>
<figure>
    <img src="/w/react-on-itch/work.png" href="/w/react-on-itch/work.png" alt="An itch.io draft game page.">
    <figcaption>Hooray! The default create-react-app page is showing as expected!</figcaption>
</figure>
<p>Hopefully this has helped you if this was a problem you encountered.</p>
<p>ReactJS is certainly far from being a common framework used for online games, but there are a fair few examples of great React games to be found on itch.io.
    Here are a few I liked:</p>
<ul>
    <li><a href="https://raespark.itch.io/hacker-forest">hacker-forest</a> - great puzzle game with simulacra of hacking into a website.</li>
    <li><a href="https://bobby-saul.itch.io/turkey-hunt">Turkey Hunt</a> - classic arcade hunting game.</li>
    <li><a href="https://thathurtabit.itch.io/doop">doop</a> - really well polished puzzle game about rotating layers.</li>
    <li><a href="https://dcshiller.itch.io/happy-mice">Happy Mice</a> - curious little simulator of mice life and death.</li>
    <li><a href="https://vedang-javdekar.itch.io/minesweeper-clone">Minesweeper Clone</a> - very clean minesweeper clone.</li>
    <li><a href="https://swilliamsio.itch.io/valyrian-wordle">Valyrian Wordle</a> - a Wordle clone, but in High Valyrian, a language of Game of Thrones.</li>
</ul>
]]></content>
</entry>
<entry>
<title>GMTK Game Jam 2021</title>
<link href="https://www.swilliams.io/w/gmtk-game-jam-2021" />
<id>https://www.swilliams.io/w/gmtk-game-jam-2021/</id>
<updated>2021-08-15T01:01:01Z</updated>
<content><![CDATA[<p>The GMTK game jam was held between June 11th and June 13th this year. It was one
    of the largest game jams of all time, with 5'761 games built during the weekend.
    I endeavoured to create a game from scratch, writing all the code myself and
    making all the assets with what small amount of artistic talent I have. For the
    SFX and music, a fantastic composer offered their services - something I greatly
    appreciated!
</p>
<p>
    I used a simple JavaScript game engine called KaboomJS. I have nothing but praise
    for this engine. It's super simple to get into, with a fantastic website detailing
    its API with dozens of examples to guide you. It being JS is a massive boon to
    me personally since it means its easy to develop using VSCode, as opposed to
    something bulky and slow like Unity, and there's a huge wealth of tutorials and
    libraries for JS that other game engines lack. The engine is, admittedly, a bit
    limited, with effectively no physics to speak of, but for the simplistic arcade
    games I'd choose to develop in a game jam, it is as perfect as can be.
</p>
<p> My result of the jam was a hectic bullet hell-like space shooter called Triple Threat.
    It got a surprising amount of praise; a lot of people said it was an interesting
    concept and quite fun once they got the hang of the controls, and of course many
    people praised the fantastic music and sound in the game. The greatest compliment
    I got was knowing a few people were able to smash my very own highscore - one
    got a monumental score of 560, well over double what I ever achieved at my own
    game!
</p>
<figure>
    <img src="/w/gmtk-game-jam-2021/TripleThreat.png" href="/w/gmtk-game-jam-2021/TripleThreat.png" alt="Screenshot from Triple Threat."
        style="width:500px;">
    <figcaption>The player ship, on the left, is composed of three separate components, responding
        to the Game Jam's theme of "Joined Together".</figcaption>
</figure>
<p>After the jam, I cleaned it up a little, responding to some player feedback regarding
    the controls and implementing music and SFX mute button for accessibility reasons.
    Overall, it was a fantastic learning experience and I loved working with everyone
    and seeing other people's creations.</p>
<p>I also uploaded the game to Newgrounds, which is a great feeling. I've used Newgrounds
    to play online games since I was a kid, and I know young me would be super happy
    to hear he eventually managed to get something he made up there.</p>
<h2>Links</h2>
<ul>
    <li><a name="fn-1" href="https://swilliamsio.itch.io/triple-threat">Play it on Itch!</a></li>
    <li><a name="fn-2" href="https://www.newgrounds.com/portal/view/804804">Play it on Newgrounds!</a></li>
    <li><a name="fn-3" href="https://github.com/s-williams/Triple-Threat">Link to the Github repository.</a></li>
</ul>
]]></content>
</entry>
<entry>
<title>Using makesite.py</title>
<link href="https://www.swilliams.io/w/using-makesite-py" />
<id>https://www.swilliams.io/w/using-makesite-py/</id>
<updated>2021-01-12T01:01:01Z</updated>
<content><![CDATA[<p>I think every software developer, or even everyone entirely, should have a personal website. They are great opportunities to learn new technologies, get your
    writing online to where people can see it, and give back to the online community no doubt you rely on.</p>
<p>I've had a personal website for a few years now and I've been using a static site that's been manually maintained and curated, which hasn't been ideal as
    I've had to spend an equal amount of time in boring upkeep as I did actually producing content. So, I'm rather pleased to have recently found <a
        class="inline" href="https://github.com/sunainapai/makesite">makesite.py</a> - an elegant, lightweight, and friendly little tool to generate static
    websites.</p>
<p>The primary design philoshopy behind makesite.py is its simplicity. The entire engine behind it is a single 250 line Python file which is easy for the even
    beginner programmers to understand. This engine uses templates you've made and combines them dynamically with content and data you've written, compiling it
    into a static website ready to be hosted. Running the code takes less than a second so the results are immediate, and so it fits nicely into my current
    workflow using VS Code.</p>
<p>Before now, I was manually maintaining the website's headers, page meta tags, RSS feed, etc. Now, I barely even have to think about that stuff and I'm more
    freed to write content without managing quite so much of the chores.</p>
<p>There are of course other static site generators out there: <a href="https://github.com/jekyll/jekyll">Jekyll</a>, <a
        href="https://github.com/getpelican/pelican">Pelican</a>, and <a href="https://github.com/gohugoio/hugo">Hugo</a> to name a few. But I think most
    struggle to come close to the simplicity of <a href="https://github.com/sunainapai/makesite">makesite.py</a>. I recommend makesite.py to anyone who's
    looking for a static site generator that is as simple as possible or anyone willing to dig a little into the code to really get a bespoke solution.</p>
    ]]></content>
</entry>
<entry>
<title>A solution to one RSS inconsistency</title>
<link href="https://www.swilliams.io/w/solution-to-rss-inconsistency" />
<id>https://www.swilliams.io/w/solution-to-rss-inconsistency/</id>
<updated>2020-06-01T01:01:01Z</updated>
<content><![CDATA[<p>RSS feeds are a beautiful and wonderful open standard that anyone can, and should, utilise on their blogs, websites, or anything else. The standards RSS
    follows are reasonably flexible, but there is another aspect to RSS that is incredibly inconsistent across all use cases. That inconsistency is the location
    of the RSS feed file and the name of that file.</p>
<p>A common idea seems to be putting a file after the domain root, though there is very little consensus on what this file should be called. Whilst
    <code class="inline">feed.xml</code> is certainly popular, I've seen <code class="inline">atom.xml</code>, <code class="inline">posts.xml</code>,
    <code class="inline">feed.atom</code> and <code class="inline">rss.xml</code>.<sup><a href="#fn-1">[1]</a></sup> Additionally, websites which have blogs
    located in a subfolder of the site, such as, commonly, <code class="inline">/w</code> or <code class="inline">/writing</code> will generally, though not
    always, have the feed be in that folder and not in the site index. </p>
<p>Looking at the big corporations. WordPress blogs can find their RSS feeds at <code class="inline">/feed</code>, which is a technique a fair few personal
    blogs employ as well<sup><a href="#fn-2">[2]</a></sup> and this is the system other blogger platforms use, such as dev.to. Meanwhile, Reddit has feeds all
    over the place - every user, subreddit, or post has a feed associated with it which you can find by adding <code class="inline">/.rss</code> to the URL.
</p>
<p>Its impossible to list all the possible varieties, and I've only just scatched the surface. I highly recommend you read through the responses to <a
        href="https://news.ycombinator.com/item?id=22800136">this hacker news post</a>, where hundreds of people have shared their blogs, to find yet more
    interesting instances of RSS implementation.</p>
<p>It's very apparent that there is no standardisation as to where and how you should place your RSS feed in your site structure, which is an issue that might
    potentially cause confusion for visitors to your website. The solution I've used for this website is to place redirects everywhere a potential user might
    look for an RSS feed. These redirects point to a single <code class="inline">atom.xml</code> file. Below is the gulp task which does this. </p>
<code class="block">var rssStrings = [ 'atom', 'feeds', 'feeds.xml', 'feed.xml', 'rss', 'rss.xml' ];
gulp.task('rss', function() {
    let tasks = [];
    for (let i in rssStrings) {
        tasks.push(
            gulp.src("dist/feed/**")
            .pipe(rename(function(path) {
                path.dirname = rssStrings[i];
            }))
            .pipe(gulp.dest("./dist"))
        );
    }
    return mergeStream(tasks);
});</code>
<p>Where <code class="inline">dist/feed/**</code> points an HTML file which provides a redirect to the <code class="inline">atom.xml</code> which contains the
    feed. Potentially, you could also copy the atom file itself, instead of a redirect. But I decided I'd rather ensure that there is only one atom file I need
    to worry about. Using this method, access to the RSS file is as available as possible.</p>
<h2>Footnotes</h2>
<ol>
    <li>A few examples: <a name="fn-1" href="https://writing.markchristian.org">writing.markchristian.org</a>, <a
            href="https://paulstamatiou.com/">paulstamatiou.com</a>, <a href="https://xkcd.com/">xkcd.com</a>, <a
            href="https://what-if.xkcd.com/">what-if.xkcd.com</a>, <a href="https://rachelbythebay.com/w/">rachelbythebay.com/w/</a> </li>
    <li>A few examples: <a name="fn-2" href="https://tkainrad.dev">tkainrad.dev</a>, <a href="http://extremelearning.com.au">extremelearning.com.au</a>, <a
            href="https://walkedtheblueline.com">walkedtheblueline.com</a>, <a href="https://www.caribbeansignal.com">caribbeansignal.com</a> </li>
</ol>
<p>As an aside, I also saw one instance (<a href="https://paul.copplest.one/">paul.copplest.one</a>) where the RSS feed was a link to the GitHub commits feed,
    which I suppose is innovative in its own sort of way.</p>
    ]]></content>
</entry>
<entry>
<title>Xamarin Note Taking App</title>
<link href="https://www.swilliams.io/w/xamarin-note-app" />
<id>https://www.swilliams.io/w/xamarin-note-app/</id>
<updated>2020-03-31T01:01:01Z</updated>
<content><![CDATA[<p>As coronavirus destroyed the world and sent everyone into social isolation, I decided to utilise the time indoors to learn Xamarin, a Microsoft .NET
    framework that allows you to create cross-platform Apps easily. I saw there was a Xamarin 101 tutorial online that talked you through creating a notes app,
    and, having walked myself through it, I found myself with the desire to improve the app in that tutorial further, since the tutorial app doesn't even allow
    you to edit notes! So I forked it and began my own version.</p>
<figure>
    <img src="/w/xamarin-note-app/main.png" href="/w/xamarin-note-app/main.png" alt="Screenshot of the main page of the app." style="height:400px;width:250px;">
    <figcaption>Screenshot of the main page of the app, showing a text box to create a new note and a list of already created notes. The layout was configured
        such that the save button will appear above the keyboard when typing a new note, thus allowing the user to more quickly save a new note.</figcaption>
</figure>
<figure>
    <img src="/w/xamarin-note-app/detail.png" href="/w/xamarin-note-app/detail.png" alt="Screenshot of an individual note page."
        style="height:400px;width:250px;">
    <figcaption>Screenshot of an individual note page inside the app. Here a user can edit or delete already created notes. Unlike in the main page, where the
        save button was moved to be more accessible, here I kept the buttons on the bottom. I figured that since there is no "are you sure" pop-up when deleting
        a note, it was better that the delete button required more effort to press.</figcaption>
</figure>
<p>Changes I made include: allowing the editing of notes; adding a timestamp to each individual notes which shows when the note was last edited; adding a title
    to each note which is the first line of the note or the first 20 characters, which is displayed in the notes list; adding the ability to delete notes; and
    restyling the app so its a bit more user friendly. There's still a few more things I want to do, like adding the ability to switch to a night theme and
    adding persistent storage, but I'm happy with the progress I've made and I'll try getting it to the app store soon.</p>
<p>Through this project, I hoped to get a better grip with C# and dive into the Xamarin toolset, and I'm pleased to say that that was successful.</p>
<h2>Footnotes</h2>
<ul>
    <li><a name="fn-1" href="https://dotnet.microsoft.com/apps/xamarin">Xamarin homepage</a>.</li>
    <li><a name="fn-2" href="https://github.com/s-williams/XamarinNoteApp">GitHub repository for this project</a>.</li>
</ul>
]]></content>
</entry>
<entry>
<title>Melbourne Girls In Tech, Hacking for Humanity - Hack the Future</title>
<link href="https://www.swilliams.io/w/hack-the-future-2019" />
<id>https://www.swilliams.io/w/hack-the-future-2019/</id>
<updated>2019-11-03T01:01:01Z</updated>
<content><![CDATA[<p>1.7 million Australians are unaware that they may already have kidney disease? How do we reach as many of these people as possible and urge them to get a
    kidney health check before it is too late?</p>
<p>That is the question asked by Kidney Health Australia at the Hacking for Humanity hackathon organised by Girls In Tech which I tackled alongside four team
    members. Our solution was a automated chat bot using Google's DialogFlow technology to assess user risk level, combined with a fierce marketing campaign
    designed to reach as many people as possible whilst being financially efficient.</p>
<figure>
    <img src="/w/hack-the-future-2019/billyTheKidney.png" href="billyTheKidney.png" alt="Billy the Kidney chat bot on Facebook Messenger."
        style="height:400px;width:250px;">
    <figcaption>Screenshot of Billy the Kidney chat bot on Facebook Messenger</figcaption>
</figure>
<p>DialogFlow makes it easy to create a natural language bot for a wide variety of platforms simultaneously, including Facebook, Instagram, and any bespoke
    location, by planning conversations through the online DialogFlow portal. Data can be extracted from conversations to a cloud instance, as it was in our
    case to calculate the users risk based on their lifestyle factors. Our demonstration at the end of the 48 hour hackathon included a live bot on Facebook
    Messenger, who we called Billy the Kidney, as well as a live demonstration on the Kidney Health Australia homepage.</p>
<p>Our solution, though not polished completely on the day, demonstrated the value of the ideas as a cost effective way for Kidney Health Australia to reach and
    impact as many people as possible.</p>
<p>The weekend also saw other charities and teams present. I was a big fan of Captain Starlight and Heart on my Sleeve, both superb organisations, the former
    brightens the day of kids in hospitals and the latter reaching out and forming genuine connections with people.</p>
<h2>Footnotes</h2>
<ul>
    <li><a name="fn-1" href="https://kidney.org.au/">Kidney Health Australia</a>. Maybe its time you had your kidney health checked?</li>
    <li><a name="fn-2" href="https://dialogflow.com/">DialogFlow</a>, the technology behind the chat bot.</li>
</ul>
]]></content>
</entry>
<entry>
<title>My new website</title>
<link href="https://www.swilliams.io/w/new-website" />
<id>https://www.swilliams.io/w/new-website/</id>
<updated>2019-11-02T01:01:01Z</updated>
<content><![CDATA[<p>Welcome to my new website, this is it. It's a beautiful, dark moded, minimalistic mess, and it's mine. This static site will hopefully fill up over the next
    few years with the wonderful accounts of my existence.</p>
<p>I needed a new website, to replace my old website,<sup><a href="#fn-1">[1]</a></sup> for a few different reasons:</p>
<ul>
    <li>My old wesbite used WordPress,<sup><a href="#fn-2">[2]</a></sup> which is boring, and the fact that I used WordPress was a crutch I relied on which
        prevented me really exploring developing a website for myself.</li>
    <li>My old website used cPanel,<sup><a href="#fn-3">[3]</a></sup> which, whilst fine for basic stuff, lacks the allure and fun of using Amazon Website
        Services, which this one is hosted on. Plus, no job requirements list has ever put "experience with cPanel required".</li>
    <li>The EU kicked me out of using a .eu domain. Yikes. I was emailed by my UK hosting provider on August 30th, 2018, that I would be losing the domain name
        in the event of a no deal Brexit on March 30th 2019, or, with a deal, on January 1st 2021. </li>
</ul>
<p>I explored a lot of options for design of my personal website - there's a lot of fantastic techy blogs out there to draw inspiration from. My favourites
    might be <a href="https://writing.markchristian.org/">this one</a>, <a href="https://austingwalters.com/">this one</a>, and <a
        href="https://rachelbythebay.com/">this wonderfully simplistic one</a>. This were all just stumbled into on HackerNews one day. I took the best ideas
    from all of them and put my own flair into them to create this place.</p>
<p>Currently, the site is written manually with Visual Studio, version controlled with a private Git repository on GitHub, prepared for upload using gulp, then
    synced with AWS via command line. All written with elbow grease except the svg logos used for the header icons which were taken from <a
        href="https://iconmonstr.com/">iconmonstr.com</a>.</p>
<h2>Footnotes</h2>
<ol>
    <li><a name="fn-1" href="http://swilliams.eu/">swilliams.eu</a>. My old website, now taken offline. I wonder if anyone else will eventually take the domain.
    </li>
    <li><a name="fn-2" href="https://wordpress.com/">WordPress</a>. Yuk, don't use WordPress. Not only does it have the problems I pointed out here, but it's
        incredibly insecure - literally just google "WordPress Vulnerabilities" and you'll see endless lists of problems with WordPress itself and its plugins.
    </li>
    <li><a name="fn-3" href="https://www.cpanel.net/">cPanel</a>, in my experience, is a simplified hosting platform designed more for non-technical people.
    </li>
    <li><a name="fn-4" href="https://aws.amazon.com/">AWS</a>, in my experience, is an endless complicated maze to get lost in.</li>
</ol>
]]></content>
</entry>
<entry>
<title>Is it better to store files in books?</title>
<link href="https://www.swilliams.io/w/storing-files-as-books" />
<id>https://www.swilliams.io/w/storing-files-as-books/</id>
<updated>2018-02-12T01:01:01Z</updated>
<content><![CDATA[<p>The lifespan of a floppy disk is 20 years, the lifespan of a magnetic tape 10 years, and the lifespan of some hard disks is only 5 years. By comparison, the
    oldest books in the world are thousands of years old, as old as civilisation itself, coming from the ancient kingdoms of Sumeria and Egypt. Evidently, books
    are much more durable than even our most durable technological storage. Therefore, do we owe it to our future generations to start backing up our digital
    media as 1s and 0s in books, ensuring that they'd last thousands of years in physical form?</p>
<figure>
    <img src="book.jpg" href="book.jpg" alt="Picture of a book." style="height:250px;width:500px;">
    <figcaption>This is a book.<sup><a href="#fn-1">[1]</a></sup></figcaption>
</figure>
<p>An A4 piece of papercan fit approximately 5000 1s or 0s, or 5 Kb of information, if we use a 10 pt font and sensible margins. That's a disappointingly small
    amount. But we can greatly improve on this if we don't limit ourselves to having a human readable piece of paper - we can fit much more by reducing the font
    size to barely readable, printing double sided, and having the most minimal margins. With all these improvesments, we can expect around 50,000 bits on each
    page (6.25 KB). </p>
<p>It is hopefully quite obvious that this is not a good way of storing written words - language is a much more dense than bits. However, there currently is no
    future-proof way to ensure our music today is heard as it should be - just recording score in notes and bars doesn't capture the essence of the music as it
    is heard on record or in mp3s. Is this the best way of recording music for future generations? MP3s are typically 128 Kb/s. Applying that to our bits per
    paper means a book of 4'300 pages could hold Rick Astley's Never Gonna Give You Up replacing the space taken up by your Song of Ice and Fire collection on
    your bookshelf. This is worth it. </p>
<p>But we can do better! Instead of inking 1s and 0s on paper, we can instead use hexadecimal to store four times as much information in the same amount of
    paper, cutting our Rick Astley volume to the mere size of a large book. But there's no reason to limit ourselves to only hex, we have over 100'000
    characters in unicode, each of which could represent a series of 1s and 0s, theres no limit to the compression we could achieve, potentially storing
    megabytes on a single A4! This could then easily be read back in future generations with advanced OCR technology, so long as the key is stored somewhere.
</p>
<p>I'm not seriously advocating this. But... maybe...?</p>
<h2>Footnotes</h2>
<ul>
    <li><a name="fn-1" href="https://en.wikipedia.org/wiki/File:Blank_book_on_a_table.jpg">Image from Wikipedia</a>.</li>
</ul>
]]></content>
</entry>
<entry>
<title>Unity Fireworks</title>
<link href="https://www.swilliams.io/w/unity-fireworks" />
<id>https://www.swilliams.io/w/unity-fireworks/</id>
<updated>2017-03-12T01:01:01Z</updated>
<content><![CDATA[<p>In the March Southampton Code Dojo, I was tasked with creating fireworks within the 2 hour time limit and, for a change from seeing the usual JavaScript or
    Python entries, I decided to do this using Unity.</p>
<p>The scene consists of a fireworks spawner game object which randomly instantiates firework rockets around itself which move upwards and, after a random
    amount of time, explode. The firework sparkles explode outwards by the physics collision engine doing its thing since they are all spawned roughly in the
    same place. Against a backdrop of a night sky, the effect is decent.</p>
<figure>
    <img src="fireworks.png" href="fireworks.png" alt="Fireworks implemented in Unity." style="height:250px;width:500px;">
    <figcaption>Fireworks implemented in Unity!</figcaption>
</figure>
<p>Obviously, since everything is a cube, this Unity scene requires a small amount of artisitic license.</p>
<h2>See also</h2>
<ul>
    <li><a name="fn-2" href="https://github.com/s-williams/Fireworks">Link to the Github repository.</a></li>
</ul>
]]></content>
</entry>
<entry>
<title>Southampton Game Jam 2017</title>
<link href="https://www.swilliams.io/w/southampton-game-jam-2017" />
<id>https://www.swilliams.io/w/southampton-game-jam-2017/</id>
<updated>2017-02-15T01:01:01Z</updated>
<content><![CDATA[<p>Over the past weekend, I participated in a Game Jam hosted at the University of Southampton during which I helped develop from scratch a game in Unity with
    four others. The game was called Jesticles and its basic premise was to walk around outside a nightclub, stopping fights between NPC's, and directing the
    agents across the road onto a bus, hopefully avoiding them being ran over. The game name is derived from the Jesticle, a cocktail from Southampton's local
    dive nightclub Jesters. It was vaguely related to the jam's theme of Revolution since if you didn't act fast enough, a revolution would occur, ending the
    game.</p>
<figure>
    <img src="jesticlesScreenshot.png" href="jesticlesScreenshot.png" alt="Screenshot from Jesticles." style="height:250px;width:500px;">
    <figcaption>Jesticles: Politicians are stumbling out of Jesters determined to start a revolution- get them safely across the road and onto the bus!
    </figcaption>
</figure>
<p>The game was my, and I suspect most of my team members', first real experience with C# and the Unity engine and it was fun adding stuff and writing code to
    fix in-depth issues wherever it was needed. Though, I must confess, how Unity handles control flow and objects is quite confusing and there is no doubt that
    we did not follow best practices during this 48 hour project. Having said that, it was incredible enjoyable.</p>
<h2>See also</h2>
<ul>
    <li><a name="fn-1" href="https://itch.io/jam/sotongamejam17/rate/118925">Link to the itch.io page.</a></li>
    <li><a name="fn-2" href="https://github.com/ImranBepari97/game-jam17">Link to the Github repository.</a></li>
</ul>
]]></content>
</entry>
<entry>
<title>ASCII Art Converter</title>
<link href="https://www.swilliams.io/w/ASCII-art-converter" />
<id>https://www.swilliams.io/w/ASCII-art-converter/</id>
<updated>2016-02-16T01:01:01Z</updated>
<content><![CDATA[<p>In the February Code Dojo, Jemma and I made an ASCII art converter in Java that takes an image as input and converts it into a text document which, when
    zoomed out far enough, imitates the original image.</p>
<figure>
    <img src="asciiConverter.png" href="asciiConverter.png"
        alt="From left to right, the output text document, the input image, and the input image converted to greyscale." style="height:250px;width:500px;">
    <figcaption>From left to right, the output text document, the input image, and the input image converted to greyscale.</figcaption>
</figure>
<p>This was done by first iterating over every pixel in the image after it has been onverted to greyscale. The luminance of every pixel was then calculated by
    using a formula found online:</p>
<code class="block">// calc luminance in range 0.0 to 1.0; using SRGB luminance constants
                    float luminance = (red * 0.2126f + green * 0.7152f + blue * 0.0722f) / 255;
            </code>
</pre>
<p>This luminance value was then stored in a very large 2D array. The array is then looped over and certain ASCII characters are chosen based on the luminance
    value selected.</p>
<p><code class="inline">diffLum</code> is the difference between the largest value of luminance found (largeLum) and the smallest value of luminance found
    (smallLum), the addition of this as opposed to set values between 0 and 1.0 mean that the whole range of characters is still used even if there is not a
    great difference in the source image's luminance values.</p>
<code class="block">// choose brightness threshold as appropriate:
if (luminance <= (diffLum*0.1) + smallLum) {
    chars[x][y] = ' ';
} else if (luminance <= (diffLum*0.2) + smallLum) {
    chars[x][y] = '.';
} else if (luminance <= (diffLum*0.3) + smallLum) {
    chars[x][y] = ':';
} else if (luminance <= (diffLum*0.4) + smallLum) {
    chars[x][y] = '-';
} else if (luminance <= (diffLum*0.5) + smallLum) {
    chars[x][y] = '=';
} else if (luminance <= (diffLum*0.6) + smallLum) {
    chars[x][y] = '+';
} else if (luminance <=(diffLum*0.7) + smallLum) {
    chars[x][y] = '*';
} else if (luminance <= (diffLum*0.8) + smallLum) {
    chars[x][y] = '#';
} else if (luminance <= (diffLum*0.9) + smallLum) {
    chars[x][y] = '%';
} else {
    chars[x][y] = '@';
}
                </code>
</pre>
<p>The chars array is then looped over and written into a text, .txt, file.</p>
<p>The solution works adequately however the output cannot be resized and so the source image must be downsized quite a bit before it is truly useful as an
    ASCII converter.</p>
    ]]></content>
</entry>
<entry>
<title>Global Game Jam 2016</title>
<link href="https://www.swilliams.io/w/global-game-jam-2016" />
<id>https://www.swilliams.io/w/global-game-jam-2016/</id>
<updated>2016-01-31T01:01:01Z</updated>
<content><![CDATA[<p>At the University of Southampton Global Game Jam 2016 event, I helped develop an isometric real time strategy town management game where you must balance
    resource management with your relationship to the gods. I joined a team which participated last year and used a game engine that was developed in last years
    game jam. The finished product was called Vikings vs Gods: Cheese fortress and was overall a quite good game, though a little lacking in high fidelity
    graphics.</p>
<figure>
    <img src="vikingsVsGodsScreenshot.png" href="vikingsVsGodsScreenshot.png" alt="Screenshot from Vikings vs Gods: Cheese Fortress."
        style="height:250px;width:500px;">
    <figcaption>Screenshot from Vikings vs Gods: Cheese Fortress</figcaption>
</figure>
<p>It the 48 hours which spanned the jam, I helped develop the basic quests and gods outline and tried to overall polish the game through adding unique mouse
    cursor, splash/intro screen, win screen, death screen, as well as fixing some issues that were left over from when the game engine was used last year.</p>
<h2>See also</h2>
<ul>
    <li><a name="fn-1" href="https://globalgamejam.org/2016/games/vikings-vs-gods-cheese-fortress">Link to the Global Game Jam page.</a></li>
    <li><a name="fn-2" href="https://github.com/mikejewell/cheesefortress">Link to the Github repository.</a></li>
</ul>
]]></content>
</entry>
<entry>
<title>How to extract images from a Word Document</title>
<link href="https://www.swilliams.io/w/extract-images-from-word" />
<id>https://www.swilliams.io/w/extract-images-from-word/</id>
<updated>2015-10-06T01:01:01Z</updated>
<content><![CDATA[<p>After Googling this question and being disappointed at how much work was needed to find a good answer, the only useful post was this old post<sup><a
            href="#fn-1">[1]</a></sup> for Word 2007, here is a definitive blog post about the topic. This post assumes use of a Microsoft Word 2013 though the
    method would work on almost any word processor.</p>
<p>The by far simplest method is to save the document as a webpage. This is done by choosing the correct file type when saving as a new file. This is done as
    normal when saving the file but choosing a different "save as type". Below is a screenshot of saving a document called "Example".</p>
<figure>
    <img src="savingAsWebPage.png" href="savingAsWebPage.png" alt="The save screen when saving the document." style="height:250px;width:500px;">
    <figcaption>The save screen when saving the document. Click to view full size.</figcaption>
</figure>
<p>Next, simply navigate to the folder where you saved the document as a webpage and you should find a new folder called "Example_files" alongside the new .htm
    file. Inside the folder will be every photo within the document in a .jpg format along with some meta data and other stuff you dont need to worry about.</p>
<p>Hooray!</p>
<h2>Footnotes</h2>
<ol>
    <li><a name="fn-1" href="https://cnedelcu.blogspot.com/2013/02/top-3-ways-to-extract-images-from-word-docx-doc-document.html">Link to older blog post on
            this subject.</a> It goes into other methods including opening the .docx as a .zip and manually saving each image. However, these are much less time
        efficient.</li>
</ol>
]]></content>
</entry>

</feed>
