AI & ML DevOps General Tech Community Best Practices & Tools All News About Contact
advertisement
General Tech

Recently Played: bringing back my Last.fm component

May 2026 8 min read
Back to General Tech

Around 2010, my personal website had a Last.fm widget showing what I'd been listening to. It was a small thing, just some album covers and track names in the sidebar, but it was very me. Back then, your personal space was an extension of yourself, and having your musical taste in the corner felt good.

Fast forward fifteen years and I've rebuilt this site from scratch several times. The Last.fm widget never made the cut again. Not for any good reason: it just disappeared from the list every time. When I was working on the sidebar recently, I remembered it and thought: why not bring it back?

There's something I like about full circle. The web has gone through its various phases since 2010 (the years of single-page applications, the years of JavaScript for everything) and has come out on the other side again valuing progressive enhancement and server-rendered HTML. My site is built with Eleventy and deployed from GitHub Actions to S3 with CloudFront. It seemed appropriate to bring back a feature from an earlier era, built on the principles that matter to me now.

That's how it works.

the architecture

The component is a two-tier system: compile-time SSR for the initial HTML and client-side polling for live updates. Placed in the sidebar as <listening-now>

WebC element next to <my-details>

.

Build time: listening.js → Last.fm API → trace data → SSR in HTML

Runtime: JS Client → polls Last.fm API every 60 seconds → diffs → updates DOM

├── Local storage cache (2 minute TTL) for instant uploads

└── pauses when tab is hidden, resumes when focused

Data layer: src/_data/listening.js

This is an Eleventy global data file that runs at build time. Call the Last.fm API, specifically user.getrecenttracks.

- requesting the five most recent tracks for my account.

It has a three-second timeout so that if Last.fm is having a bad day, builds aren't suspended. On error, returns {hints: []}

, meaning builds are never interrupted regardless of what the API does.

Raw API response is mapped to clean objects: name

, artist

, album

, URL address

, art

, and a NowPlaying

boolean. All text fields are HTML escaped and URLs are validated (http only:

and https:

allowed schemes) – This is the server-side XSS protection layer.

The data is then available to templates such as listening.tracks.

through the Eleventy data cascade.

The component: now-listening.webc

The WebC component has three distinct parts.

Server-rendered HTML

A webc:type="render"

The script runs at build time and reads this.$data.listening.tracks

. Generates the initial HTML so that the page is sent with real tracking data embedded. This means that the content is visible immediately after loading, and for search engines or anyone browsing without JavaScript, this is the component. It's done. No spinner, no empty state.

A <template>

element

An inert HTML template used by client-side code for DOM cloning. Defines the tracking row structure with data-*

attribute hooks - data tracking

, data-name

, data detail

, data art placeholder

and data in playback

. Nothing is generated from this until JavaScript picks it up.

Client-side polling script

A self-executing IIFE (kept alive with webc:keep

to prevent Eleventy from removing it) which handles the live behavior:

Poll: Access the Last.fm API every 60 seconds to keep the track list updated.

Local Storage Cache: On page load, if a new cache exists (two-minute TTL), it is processed from the cache immediately and defers the first API call. This avoids the problem of stale content on repeat visits or when navigating between pages.

Differentiation: compare JSON.stringify (hints)

against the last known state. If nothing has changed, the DOM remains intact. No unnecessary reflux.

Visibility awareness: listen to the visibility change

to stop polling when the tab is hidden and restart when it becomes visible. If you leave the

Related Coverage

General Tech

Top 7 Featured DEV Posts of the Week

General Tech

3 words worth a billion dollars: Drift to Determinism (DriDe)