Back home
The building of my website in three panels. In the first panel, the bunny is working on design files, while the alien stands next to her pointing at something on her screen. In the next panel, the alien is working alone on his laptop. The third and final panel shows the alien's laptop close up, with a rendition of the website shown on its screen.

My shiny new website

After a long period of feeling my website was lacking flair, I finally took the time to create something I'm happy with.

To avoid any confusion, I should probably mention this essay took a while to publish; the redesign has been live for a month already.

I bought the vrugtehagel.nl domain in 2013. Since then, it has seen a variety of different forms. The first few years, it mostly served as a project dump for small games, posts, or mathematical explorations. In 2020, I pivoted to a portfolio to help me get a job. It was marine-themed, with fun subtle effects and animated SVGs. That version served me well for a couple of years, until I did the unthinkable and absent-mindedly ran rm -rf / on my server. As one might expect, that deleted my website, along with the rest of the system, including all my side projects. At that point I had learned about the wonders of version control, and the site itself was safely stored in a git repository, but many of the linked projects were not, and were thus irretrievably lost to time. Given I no longer had a reason to market myself to potential employers, and I would have to do some significant alterations to the site to make it make sense without the now-deleted side projects, I decided to put up a placeholder for the time being. That placeholder was there for far too long, and I eventually put up some simple text-only versions so I could get some blog posts out, but none of these sites really scratched my creative itch. To be frank, I was a little embarrassed with the state of my site.

So eventually, I snapped, and I decided it was time to sit myself down and build something nice.

Step 1: an idea

I usually do all of my "designing" myself. That is, I try to build things in a way that both the user experience and looks are decent. For this project, I wanted something truly special, so I figured this would be the perfect opportunity to ask someone near and dear to me for help: Jina. She's an amazing designer, much better than she gives herself credit for, and given our close relationship, I thought this would be a great chance to work together. I explained to her that I was struggling to find a creative direction, but that I'd like to do all the high-level details myself, so that it would still express my own personality.

After a few different ideas, she proposed something that clicked right away. A sort of continuous story, playful, but to-the-point. This had the vibe I was looking for, and was intriguing from a technical standpoint. For the style she suggested I re-use a comic I drew a few years ago, involving a long-necked alien meeting a bunny character. The alien in the original comic was not only already intended to represent myself, but also was a great choice for the animations. Not too detailed, so I can draw many different positions in a reasonable amount of time, but still unique.

Fun fact: the alien only has a left ear, because I myself am deaf on my right side. But not to worry; I do have a working nose and mouth.

Since Jina's wireframes were only conceptual (like I asked), I did some sketches myself to really flesh out the details of the "storyline", and iterated through that using her feedback, until I had something concrete to start working on. Early on, I decided that the continuous animations would be exclusive to the larger screens. On smaller screens, the text would have to overlap the imagery, and this seemed like it would result in poor usability. The distinction is unfortunate, but the silver lining is that the mobile version is a bit more lightweight since it only loads a small subset of the sprites.

Step 2: technical decisions

The technical decisions were much more clear-cut for me. If I could, I would write raw HTML, CSS and JS. Given that's not very maintainable, I chose some light-weight tools that let me craft the site exactly how I want it. This starts with Eleventy, a flexible JS-based static site generator. I then plug my favorite templating language into it, Vento. For my content, I use Eleventy's built-in Markdown engine, with a few additions to support callouts and syntax highlighting. For the latter, I use PrismJS as I find it easy to work with and it defaults to semantic HTML, just how I like it.

This setup gives me full control over what ends up on the client. Which is, first of all, no framework whatsoever (not even my own). This site just doesn't need one. Secondly, I know the site'll be somewhat illustration-heavy, but there's no excuse for sending bundles of unnecessary CSS or JavaScript. Especially tracking; I value my own privacy beyond convenience, so there will be zero tracking, none whatsoever. What you do is your business; I'd like to keep it that way. Which brings me to my next point; disabling JavaScript. I myself sometimes do this, to protect myself, speed up websites and "mute" pesky popups. Even though my site doesn't have any of those, I figured it only makes sense to make it work with JS disabled. Any scripts must be progressive enhancements. And any non-essential CSS, too; specifically the scroll-driven animations should be. Last, but not least, I focus on writing semantic HTML to facilitate better accessibility, as well as making proactive design choices to help atypical visitors use my site.

Step 3: the imagery

The biggest unknown in this project was, by far, the drawing. I enjoy drawing, but I don't end up doing it very often, so I'll be honest and say I wasn't particularly confident I could pull this off. I loved the idea, though, so I had to try. The thing I was most afraid of was making the animations look smooth. I had no idea how many frames I would need, nor how accurate they would have to be to create decently smooth-looking movement.

As for implementation, I figured the easiest way to compose the animations is to have the section-specific frames as separate images, and the transitions between them as sprite sheets. This way, if the animations aren't shown in a particular browser, the additional frames aren't loaded unnecessarily. For the sprite sheets, I chose to animate them by setting the size of the <img> to the dimensions of a single frame, and together with object-fit: cover, this allows for "cycling" through frames using the object-position property. This lets me avoid JavaScript entirely, at least for the animation portion of things.

There are several animated image formats, like GIF or APNG, but this type of image is not a great fit for the purpose of a scroll-driven animation because their playback cannot be controlled from CSS (even doing so with JS isn't trivial).

In the meantime, I got a little distracted by the thought of creating my own custom image format. The images I was producing were strictly four-tone (they weren't anti-aliased) and the style lacked intricate textures, thus I figured I could come up with a lossless format that would perform better than the more general formats like WebP or AVIF. I will spare you the details, because those've been dedicated to a whole different essay, but long story short, I did, called it "SN", and it is about 30% better than PNG (even beating 0-quality WebP). The final implementation of this is powered by a service worker, which intercepts requests to .sn.png files, then fetches the .sn version instead, and converts it to a PNG using an inlined hand-crafted WASM module. Of course, this is a progressive enhancement; if JS or service workers are unavailable, then the (slightly bigger) PNGs are fetched instead. In practice, this saves visitors to my homepage about 75kB. Neat, right?

Could this be the first time that allowing JavaScript on a website makes it smaller?

All that aside, the actual process around creating the images was much smoother than I anticipated. Besides conceptual missteps, I never had to edit a spritesheet after the fact. I worked mostly off of intuition, drawing the intermediate frames that I thought had to be included for the actions and movements to be clear (about 3-5 frames depending on the animation), and I think things turned out well!

Step 4: implementation

I mentioned the object-position trick earlier, which let me "animate" a sprite sheet in an <img>. In practice, each animation was extracted in one or more @keyframe animations. I say "one or more", because I ended up (mostly) splitting the parts with different timing functions into their own @keyframe declaration. Specifically, the positional movements need to be "smooth" whereas the object-position needs to be stepped through discreetly. The showing and hiding of each element was done using visibility, which is animatable, but its values are discreet, so these could be added into whichever @keyframe was the easiest to mix them into. The movements themselves are then controlled using a variety of different properties, like inset, translate, margin, and offset. Each part of the whole had relatively unique requirements, so things were implemented on a case-by-case basis.

All the @keyframe declarations are separated into a separate stylesheet, which isn't loaded at all if the window is too small for the animations to run, or if both JS and scroll-driven animations aren't available. In particular, Firefox doesn't yet support animation-timeline: scroll(), so I've included a JS-based fallback there to allow the animations to run anyway. The fallback works by pausing all the animations and then controlling them using a negative animation-delay matching the relative scroll position on the page. Not particularly pretty, but simple, and works quite well.

I also thought it would be fun to include a header image for each of my posts, both because it adds some playful pizzazz to each essay, but also to motivate myself to draw more, and to raise the bar for quality. I did also remove some of my older posts, as they were more "rants" than informative pieces, although I'm considering rewriting some of them in a more constructive manner.

The result

I'm super excited about the result, and I'm happy to finally be truly proud of my website again. I think it reflects me and my personality well, and feel very thankful to Jina for all the ways in which she supported me throughout this project. I hope visitors enjoy the result as much as I did working on it!