Cathode, or, My Favourite Music from 2015

January 03, 2016

As a school hol­i­day pro­ject, I made a Hugo site to doc­u­ment all of my favourite music from 2015, and released the theme sep­a­rate­ly as a stand­alone called Cath­ode. Check the site out here, and the theme here.

Note: I’m writ­ing this post con­cur­rent­ly, so it’ll be kinda be stream-of-con­scious­ness.


Objec­tives:

  • Learn some­thing new
  • Doc­u­ment my favourite music

I have about 4-5 days.

Learn some­thing new

I looked through a list of static site gen­er­a­tors over at https://​www.sta­t­ic­gen.com/ and picked the high­est-rank­ing one that was writ­ten in a lan­guage I wasn’t already famil­iar with. If I have pick up a new lan­guage, this will be a great moti­va­tor. I picked Hugo.

I also wanted to learn PostC­SS, having heard many great things in the past. I’ve become very com­fort­able with SASS, Com­pass, and Susy. A little too com­fort­able.

Doc­u­ment my favourite music

I could have done it the tra­di­tion­al way: 1/​2-column layout, top 20 albums, top to bottom, that’s it. Easy to do, a no-brain­er with a blog-aware gen­er­a­tor like Hugo. Also, at around this point I real­ize how ridicu­lous­ly fast Hugo is.

I want to repli­cate iTunes’s album view inter­face. I think it’s pos­si­ble - might be quite chal­leng­ing, but it’ll be fun. Well-defined end goal is also a good thing. Does it make sense for the web?

iTunes's album view interface
iTunes's album view interface

Per­for­mance

The site will be image-heavy, because album art. But it needs to be as fast as I can make it in the time I have. Image opti­mi­sa­tion, yup, Ima­geOp­tim - lazy load­ing, yeah, I can do that I think, but I’ll leave the dif­fi­cult parts to the last.

jQuery

jQuery is like fast food. It gets you your nutri­tion, and fast. It’s not good for you in the long run, but I only have 4 days; I need to pick my bat­tles. Also, I don’t want to waste one whole day set­ting up Web­pack or any dev work­flow, so no ES6. I hate ES5 so this will force me to write as little JavaScript as pos­si­ble. This is a good thing because per­for­mance and because I’m work­ing on a Redux and Express pro­ject con­cur­rent­ly and I’m find­ing out that it’s pos­si­ble to OD on JavaScript, even with ES6 and fancy exper­i­men­tal ES7 fea­tures.

Tem­plat­ing with Go

Hugo uses Go’s html/​tem­plate library. Having used my fair share of tem­plat­ing lan­guages - ERB, Han­dle­bars, DTL (Django tem­plate lan­guage), Liquid, Slim, Jbuilder - Go tem­plates by far uses the weird­est syntax I’ve ever seen. This is an actual exam­ple from their docs:

{% raw %}
{{  isset .Params "caption" | or isset .Params "title" | or isset .Params "attr" | if }}
{% endraw %}

I’m sure part of it is because I don’t actu­al­ly know much Go. I really like the tem­plate func­tions though, because they remind me of Haskell.

Colours

A big part of iTunes’s album view UI is the colour tran­si­tion. Thank­ful­ly, some­body already did all the hard work, and even made a JavaScript port of it. It’s called Col­ib­ri, and is meant for use in the brows­er. I sug­gest read­ing the first link - it’s damn cool (and I’m lucky that I don’t have to do any of that).

I went with that for a while, but it turns out that it’s a pretty expen­sive com­pu­ta­tion - each image takes about 200-300ms to com­pute, which quick­ly adds up. After exper­i­ment­ing with dif­fer­ent ways to keep the UI respon­sive, I gave up and mod­i­fied Col­ib­ri to work with Node instead.

Col­ib­ri uses the Canvas API, so to pre­com­pute the colours I need a Node equiv­a­lent of Canvas, which came in the form of node-canvas. It also requires installing Cairo. After doing all that, I manage to get Col­ib­ri to run with Node. I store its output as a JSON file in the data folder, which Hugo can access (under the .Site.Data key) when com­pil­ing its tem­plates. I store the colour info as data attrib­ut­es so the JavaScript can access that.

An example of how the colours are precomputed and stored.
An example of how the colours are precomputed and stored.

I also include a fall­back (as a theme option) so that users can con­tin­ue to use Col­ib­ri in the brows­er if for some reason they cannot pre­com­pute the colours.

PostC­SS

PostC­SS is great! I used Lost as my grid system, and having used Susy for a while now I can say that Lost can com­plete­ly replace Susy in my future CSS work­flow. My entire PostC­SS stylesheet, includ­ing respon­sive styles, comes up to only 128 lines. It’s incred­i­bly pro­duc­tive. PostC­SS pro­cess­ing is also gen­er­al­ly includ­ed as part of a larger work­flow (web­pack, Gulp, etc) but since I’m not using any of that I opted to use postcss-cli instead, which works great as a stand­alone watch-and-com­pile work­flow.

CSS Tran­si­tions

For colour tran­si­tions I orig­i­nal­ly used jQuery Color (when you’re using jQuery, every­thing looks like a prob­lem to be solved with a jQuery plugin), but quick­ly switched out to using CSS3 tran­si­tions instead, which makes it notice­ably faster. The only remain­ing major part I’m using jQuery for is the slideUp and slideDown tran­si­tions. I don’t think it’s very dif­fi­cult to make a CSS3 ver­sion of its behav­iour (which is a little more nuanced than tran­si­tion­ing the height prop­er­ty), but it’ll do for now. This will def­i­nite­ly be the next major thing to do, if I con­tin­ue to work on it.

Lazy load­ing

At this point, the site is almost done. It’s going to have a lot of images, but worse than that it’s going to have a lot of Spo­ti­fy and Youtube iframe embeds. They do much more damage to per­for­mance. If I’m going to find a lazy load­ing solu­tion for images, it has to work for iframes as well. lazy­load by vvo comes out pretty highly on the Google, and it works for iframes as well. Cool. I hope this works. I change my Youtube and Spo­ti­fy short­codes to include the data-src and onload attrib­ut­es as required by lazy­load:

<iframe src=about:blank data-src="https://embed.spotify.com/?uri={{ .Get 0 }}" height="400" frameborder="0" allowtransparency="true" onload=lzld(this)></iframe>

as well as the images:

<img class="album-thumbnail" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" data-src="/images/{{ .Params.cover }}.jpg" onload="lzld(this)">

while using a place­hold­er 1px gif loaded in base64. I’m not sure if this is a better idea than just using an actual .gif file.

I refresh the page and mon­i­tor the request count. I scroll - the request count jumps. Great! I click on an album to expand its con­tent. The request count jumps again, and the empty space is sud­den­ly pop­u­lat­ed with the iframe. Holy shit! This is way easier than it should be. Thanks vvo. Open source is the best thing ever.

Writ­ing

That took me a little more than 2 days to do. Now that I’m actu­al­ly done, it’s time to write the actual con­tent.

This actu­al­ly ended up taking way more time than making the site, because I had to write some­thing a little more than “This shit is really good. Go listen to it.” This is my first time “review­ing” music, so I don’t have a work­flow for doing this except going through my saved albums on Spo­ti­fy and Youtube and con­sol­i­dat­ing and relis­ten­ing to every­thing and just bash­ing words out. I def­i­nite­ly need to improve my writ­ing and use more dif­fi­cult and descrip­tive words.

I’d also like to clar­i­fy that the rel­a­tive pauci­ty of main­stream or pop albums in the “Albums” sound­track does not indi­cate that I’m nec­es­sar­i­ly hip­ster or just being deviant for its own sake (okay fine, maybe it does a bit). What’s actu­al­ly hap­pen­ing here is that most main­stream or pop albums, like I men­tioned briefly in my review of Dopamine, are “car­ri­ers” for sin­gles. So even though the sin­gles are good, most of the time the rest of the album just does­n’t match up. As a result, the album won’t be includ­ed, but the afore­men­tioned sin­gles will end up in the “Sin­gles” cat­e­go­ry instead.

Part­ing Notes

All in all, this took me about 6 days to do, from brain­storm­ing to deploy­ment (on Github Pages). The first day was learn­ing about Hugo and how it worked, and set­ting up scaf­fold­ing and work­flow. 2 days for coding the site, and the last 3 days for writ­ing actual con­tent. I’m actu­al­ly not even done with the con­tent yet. I’ve only fin­ished the “Albums” cat­e­go­ry, but I haven’t gotten start­ed with my favourite live shows or sin­gles, so that’ll take me a while more to do.

Cath­ode is:

  • 57 lines of tem­plate code
  • 47 + 89 lines of JavaScript (not count­ing jQuery and lazy­load :/)
  • 128 lines of PostC­SS

Not that lines of code cor­re­late direct­ly to code qual­i­ty or effi­cien­cy, but still. It was a good learn­ing expe­ri­ence. I don’t think people write enough about the end-to-end process and intri­ca­cies of front-end devel­op­ment, so I hope this gives you a glimpse of what it’s like.

Thanks for read­ing!

Best of 2015 Music

Cath­ode