how the last.fm music chart works

i’ve used last.fm for ages as a passive diary of what i actually listen to — players and phones send scrobbles, and last.fm aggregates them. the chart on the music page is not a second tracker; it’s just a readout of that same account, shaped into something nice to look at on the site.

what you see on /music/

the layout is layouts/music/list.html: your markdown intro from content/music/_index.md renders first, then a fixed block with a heading, a loading state, a hidden grid container, and an error container. a single script tag loads static/js/music-chart.js (paths as in this repo), which boots a small MusicChart class on DOMContentLoaded.

the grid asks last.fm for user.gettopalbums over period=7day, which is last.fm’s notion of “top albums recently,” not a calendar week drawn on my server. the client requests limit = n × n albums so the grid stays square: 5×5 (25) on wider viewports and 4×4 (16) when window.innerWidth is at most 480px — if that breakpoint flips, the script treats it as a different shape and drops any cached payload whose stored gridSize no longer matches, then refetches.

each cell is an <a> around an <img>: the href is last.fm’s own album.url, opened in a new tab. cover art comes from the api’s image array; the code prefers largemediumsmallextralarge by label and falls back to the site mascot image when last.fm has nothing usable.

styling lives in static/css/custom.css under .music-chart / .music-chart-grid / .album-cover — mostly a bordered grid, square cells, and a light hover dim on covers.

the actual http call is a normal browser fetch to last.fm’s web api (user.gettopalbums, 7day window). nothing on this site logs your plays — it only asks last.fm for the same aggregate chart you could browse on their site.

caching and failure modes

successful responses are written to localStorage under the key music-chart-cache, with a timestamp and the gridSize used for that fetch. for 24 hours after that timestamp, a visitor can still get a grid even if the api is down or flaky: init() tries a live fetch first when that’s possible, then falls back to cache on error. if there’s no fresh data and nothing usable in storage, you get the error state instead of a half-silent failure.

there is a clearCache() method on the class for manual invalidation (handy in the console); the old internal docs mention a refresh control that isn’t wired in the current template — if you need a hard refresh today, clear site data for the domain or bump the cache key in code.

the heading text under the chart is rewritten in js to “top albums (week of mm-dd)” using sunday as the start of the week in local time — that’s a cosmetic label for humans, not the same boundary last.fm uses inside 7day.

tying it together

  • tracking: whatever apps you connect to last.fm handle scrobbles; the blog does not see individual plays.
  • aggregation: last.fm’s 7day top albums endpoint does the ranking.
  • visualization: one static page, one js module, fetch + grid css, optional localStorage resilience.

if you run a similar setup yourself, last.fm’s own api docs are the right place for keys, quotas, and method parameters.

links


scrobbles → last.fm’s weekly top albums api → a static hugo page with client-side fetch, localstorage cache, and a responsive cover grid.

2026-04-11


on this page: