A screenshot of the hermitclock.com website

A dashboard for viewing Hermitcraft member time zones.

Hermitcraft is a private, survival-multiplayer Minecraft server full of content creators. My wife and I have enjoyed watching the Hermits for years across Youtube videos and livestreams.

Due to the Hermitcraft members being spread out across the globe, a source of confusion for us has always been everyone’s local times. Whenever there are livestreams or scheduled events, we end up having to do lots of research and double-checking to make sure we know when things are happening.

To help stream-line things, I decided to put together a dashboard that shows everything we need in one place.

Goals & Features

  • Have multiple ‘clocks’ running on a single page which update once per second.
  • Track the following information per Hermitcraft member:
    • User name
    • Canonical time zone name (e.g. Europe/London, US/Central, etc)
    • In-game player skin (The design of their character)
    • Active social media and content platform accounts
  • Custom design:
    • Mobile-first
    • Modern
    • Fully accessible by keyboard and screen readers

Tech Stack

About the build

This project was quite a fun one for me for a few reasons:

  • It’s my project, which is always exciting between projects for clients.
  • It’s a small project, so it was finished quickly without the risk of burnout.
  • As I primarily work with brochure sites and blogs, this was a unique design challenge.
  • There wasn’t any pressure to write much content for the site, as it isn’t intended to be SEO performant.

Back end

This website is built with Laravel. Admittedly, as HermitClock is quite simple, there’s even an argument for not needing a back end at all. This could just as easily have been an Astro site or something similar, as all of the information could have been hard-coded. However, by going with Laravel I’ve opened myself up to having a much more maintainable project for years to come.

The database

There are two tables in the database beyond Laravel’s defaults; ‘hermits’ and ‘socials’. The ‘hermits’ table, as you might guess, has a record for each member of the Hermitcraft server. The ‘socials’ table is linked to the ‘hermits’ table with a ‘hasMany’ relationship, allowing me to associate multiple social records for each hermit. Here are the columns found on each table:

The ‘hermits’ table

NameTypeDescription
idBIG INT (Auto incrementing)The primary key
nameVARCHARTheir in-game username
iconVARCHARThe path to the icon
timezoneVARCHARThe canonical timezone name
The columns found in the ‘hermits’ table.

The ‘socials’ table

NameTypeDescription
idBIG INT (Auto incrementing)The primary key
hermit_idBIG INTA foreign key- the ID of the Hermit
nameVARCHARThe name of the social platform
urlVARCHARThe link to the social platform
The columns found in the ‘socials’ table.

By utilising the above tables, the content of the site is generated dynamically without the need for code changes when a piece of information changes. All of the information is fetched by use of Laravel’s Eloquent ORM for simple, efficient querying. Fetching all hermits and their socials, and then grouping them by their timezones so they can be displayed together based on location is as simple as:

Hermit::with('socials')->get()->groupBy('timezone');

Caching

With Eloquent ORM’s efficiency, and with the limited traffic I’d expect this site to get, I’m sure I could have gotten away with implementing no cache at all on this project. If you don’t know already, I don’t do things by halves- so I went ahead with caching my database calls anyway.

Implementing caching for this project was about as simple as it can get. I didn’t have to worry about generating the appropriate caching keys and working out the most appropriate TTL for my caches. The home page fetches all of the Hermits in the database and all of their socials. Additionally, this information isn’t going to change more than once or twice per year. As a result, I’ve got a single hard-coded caching key and have given it a lifespan of 1 hour, although I easily could have made this 24 hours if I wanted to. Instead of 4 database queries per page load, we now have 0, with the exception being the first time it’s loaded every 60 minutes.

For my database driver, I’ve gone with Redis. I’m a big fan of Redis and use it on all of my WordPress sites. It was overkill for this project, as I could have gone with using the MySQL driver to compile everything into 1 database call, but as Redis doesn’t cost me anything to set up, I felt like there was no reason not to utilise it.

Tests

Due to the small scale of the backend portion of this build, the tests were very simple to implement. For a personal project like this, I don’t strive for 100% test coverage, as that’s a bit of a myth anyway. Instead, I will write tests only for non-standard methods. I used PHPUnit, which ships with Laravel by default and can be utilised via the PHP Artisan CLI.

Front end

For the front-end portion of this build, I decided to go with Tailwind CSS. Tailwind provides a suite of utility classes that handle all of the basic functionalities of CSS. This means you’re baking your CSS into your HTML directly. If used correctly, this is great for speed and readability. While I typically prefer using Sass on bigger projects, Tailwind has allowed me to develop this much faster and has streamlined this portion of the build.

One of the most unique things about this project is the clock components dotted around the page. These all tick once per second and show the current time in different time zones. This is a custom component I’ve built, which is optimised for performance and replication. Creating a new clock is as simple as:

<div class="clock mx-auto min-h-26 max-w-max text-center" data-timezone="{{ $timezone }}">
    <span class="text-xl font-bold">{{ $timezone }}</span>

    <div class="max-w-max text-4xl">
        <span class="clock__time">00 : 00 : 00</span>
        <span class="clock__period text-xl">AM</span>
    </div>

    <div class="text-xl">
        <span class="clock__date">Month 00 Year</span>
    </div>
</div>

As you can see, there are some Tailwind CSS classes in there. However, the core of the component is that we pass a data-timezone attribute to it. This attribute can contain, for example, Europe/London. Using this, the clock will then display the time in that exact time zone. To power this, I’ve written some Javascript code which assembles all clocks on a page and, once per second, increments the value. Optimising this code was a challenge, as running a dozen clocks on one page is quite expensive for computing power. Users with slower devices would struggle, and nobody wants to use a laggy website. In the end, I’m very proud of the final product for this clock.

One of my biggest considerations, and something I’m keen on elevating my knowledge and experience in, is accessibility. Accessibility in websites is a complicated topic. Ensuring a website is efficient to navigate with a keyboard, ensuring that screen readers can ‘see’ what they need to and ignore irrelevant elements, and ensuring that associated elements such as buttons which show/hide content are all married up appropriately. By using resources such as W3C, and by carrying out explorative tests in navigating the website with only my keyboard, I believe I’ve done a good job of allowing this project to be easily used by anyone.

The future

Since the launch of HermitClock, it’s been really well received by the Hermitcraft community. I’m grateful for the positive responses and healthy traffic the website has received. As a result of the success, I’ve decided to continue development on this project. To read more about the launch of Hermitclock, check out this blog post I’ve published. For continued updates on the project, check out the category I’ve created on my blog for all Hermitclock posts.

The clock component that I built is something I’m quite proud of. One potential improvement I’m considering for the clock is to add a toggle for a more performance-optimised version which, instead of updating once per second, only updates as each minute ticks over. This would be good for users with slower devices. If I receive any feedback on performance issues, I’ll look into it at that time. I am also considering making the component usable outside of the project, as a small NPM package or something similar, but I have no plans to schedule doing that at this time.