The Crusty Dev

Rebuilding AthensNoise.Rocks | Dev Diary Number Two

In my first development diary about AthensNoise.Rocks, I wrote about setting up a self-hosted web radio using AzuraCast and building a separate website with Hugo.

The idea was not very complicated.

One small server would run the radio. Another small server would host the website. AzuraCast would handle the stream, playlists and automation. Hugo would generate a collection of static HTML pages.

No large database. No expensive infrastructure. No attempt to build the next Spotify.

Just a radio and a website.

That post was published in January 2023, although I had already been working on the project during 2022.

A few years later, the original setup is still operating.

The radio works.

The website works.

Naturally, this means I have decided to rebuild half of it.

This is the second development diary about the radio, the website and the development map I have been using while redesigning AthensNoise.Rocks.

It is not a tutorial and it is not a finished project report.

It is closer to a map of what exists, what I have changed, what still needs work and which parts have consumed more evenings than they had any right to.


The Internet Underneath the Internet

Recently I read Terry Godier’s essay, The Boring Internet.

The argument is that the internet itself is not disappearing. What is falling apart is the commercial layer that was built on top of it.

The feeds, platforms, recommendation systems, advertising networks and venture-backed destinations are not the internet.

They are tenants.

Some particularly noisy tenants have spent the last fifteen years acting like they own the building.

Underneath all of that, the actual internet is still made from fairly boring things:

A computer
    |
    |  makes a request
    v
Another computer
    |
    |  sends some files
    v
A browser
    |
    |  interprets them
    v
A web page

There are servers, domain names, protocols, text files, images, links and people trying to remember which configuration file they edited six months ago.

That is more or less how AthensNoise.Rocks is built.

The website is not an account inside somebody else’s platform. It is a directory of files on a server.

The radio is not a playlist inside a streaming company’s application. It is an audio stream produced by software running on another server.

The posts are Markdown files.

Hugo turns them into HTML.

A deployment script copies those files to the web server.

The browser downloads them.

The listener presses play.

This is the web.

It is not especially magical when explained like that, and this is one of the reasons I still like it.


The Original Setup

The original AthensNoise.Rocks infrastructure had two main parts.

                     ATHENSNOISE.ROCKS

              ┌─────────────────────────┐
              │      Hugo website       │
              │                         │
Visitor ─────▶│ Articles, pages, player │
              │ HTML, CSS, JavaScript   │
              └────────────┬────────────┘
                           │ API requests
              ┌─────────────────────────┐
              │       AzuraCast         │
              │                         │
Listener ────▶│ Stream, playlists, API  │
              │ schedule and metadata   │
              └─────────────────────────┘

AzuraCast runs the radio side.

Hugo builds the public website.

Keeping those two systems separate was one of the better decisions I made when starting the project.

I can change the website without interrupting the radio stream.

I can restart the radio server without rebuilding the blog.

If one part develops a problem, the other part generally continues doing its job.

The architecture is not sophisticated, but it is easy enough to understand after not looking at it for three months.

That is an underrated technical feature.


The Radio Server

AzuraCast still handles most of the actual radio work:

  • AutoDJ
  • Playlist rotation
  • Scheduled programming
  • Icecast streaming
  • Track metadata
  • Album artwork
  • Recently played songs
  • Podcast feeds
  • Public API endpoints
  • The administration interface

For a small independent web radio, it provides nearly everything required in one stack.

The server itself is still not particularly impressive.

It does not need to be.

There is often a tendency in development to design infrastructure for the imaginary success of a project rather than its actual requirements.

AthensNoise.Rocks does not need a globally distributed Kubernetes cluster.

It needs to play punk records without falling over.

So far, the small-server approach continues to make sense.


The Website

The public website is still generated with Hugo.

I chose a static-site generator because most of AthensNoise.Rocks is publishing:

  • Articles
  • Interviews
  • Essays
  • Reviews
  • Album streams
  • Mixtapes
  • Station information
  • Project documentation

None of that requires a database on every page request.

The content lives in plain-text files.

Hugo reads those files, combines them with templates and generates the complete website.

Markdown
   +
Hugo templates
   +
CSS and JavaScript
Static HTML files
Web server
Visitor

This makes the site fast, portable and relatively easy to back up.

It also means there is no content-management database waiting to corrupt itself on a Sunday morning.

The disadvantage is that anything live or interactive has to be added through JavaScript or generated before deployment.

Since AthensNoise.Rocks includes a radio player, schedule, now-playing information, track history, podcasts and other live data, the “static” website has slowly accumulated a fair amount of JavaScript.

Static does not mean non-interactive.

It means the server does not need to construct every page from scratch whenever somebody visits it.


Why Rebuild It?

The original site worked, but it had grown in the same way many personal websites grow.

One page was added.

Then another widget.

Then a player.

Then another version of the player because the first player did not behave correctly on mobile.

Then a schedule.

Then a second schedule because the first schedule displayed seven days in a format suitable for an air-traffic controller.

Each individual part worked, but the site had started to feel like a box containing several unrelated experiments.

The player looked like one application.

The schedule looked like another.

The track history came from somewhere else.

The podcast page had its own behaviour.

The videos depended on an external widget.

The content itself was mixed together in ways that made sense technically but not always editorially.

So the objective of the revamp is not to replace the complete system.

It is to make the existing pieces act like parts of the same website.

The main targets are:

  1. Make the radio player the main interface.
  2. Improve the schedule.
  3. Organise the published content properly.
  4. Improve the mobile layout.
  5. Reduce unnecessary third-party widgets.
  6. Keep the site compatible with Hugo.
  7. Avoid installing a database to solve problems that do not require one.
  8. Keep the project small enough that I can still understand it.

The Development Map

Instead of keeping a single task called “fix the website,” I divided AthensNoise.Rocks into several systems.

ATHENSNOISE.ROCKS
├── RADIO INFRASTRUCTURE
│   ├── AzuraCast
│   ├── Icecast
│   ├── AutoDJ
│   ├── Playlists
│   ├── Scheduling
│   └── Public API
├── LISTENING INTERFACE
│   ├── Audio player
│   ├── Album artwork
│   ├── Play and pause
│   ├── Volume control
│   ├── Now-playing information
│   └── Track history
├── PROGRAMMING
│   ├── Current programme
│   ├── Next programme
│   ├── Daily schedule
│   ├── Weekly navigation
│   └── Time-zone conversion
├── PUBLISHED CONTENT
│   ├── Articles
│   ├── Interviews
│   ├── Reviews
│   ├── Essays
│   ├── Album streams
│   ├── Mixtapes
│   └── Podcasts
├── COMMUNITY
│   ├── Contributors
│   ├── Contact
│   ├── IRC
│   └── External links
├── WEBSITE INFRASTRUCTURE
│   ├── Hugo
│   ├── Templates
│   ├── Taxonomies
│   ├── CSS
│   ├── JavaScript
│   ├── Deployment
│   └── Backups
└── PROJECT INFORMATION
    ├── About
    ├── Privacy policy
    ├── Terms of service
    └── Radio licence

The map is not technically necessary.

It does, however, stop me from opening a CSS file to change one button and emerging four hours later with a new podcast player.

At least sometimes.

It also makes the dependencies clearer.

The player depends on the AzuraCast stream and now-playing API.

The schedule depends on AzuraCast’s scheduling data.

The homepage depends on Hugo’s content filters.

The podcast interface depends on an RSS feed and browser security rules.

Everything connects to something else.


Rebuilding the Player

The player is the most important part of the site.

A radio website can contain excellent writing, interesting interviews and carefully designed pages, but the play button still needs to work.

The old player mostly relied on the standard AzuraCast public interface.

It was functional, but it looked and behaved like an external application placed inside the site.

The new version uses the AzuraCast stream and API while presenting its own interface.

The main parts are:

┌─────────────────────────────────────────────┐
│                                             │
│   ┌───────────────┐   Artist                │
│   │               │   Track title           │
│   │  ALBUM COVER  │                         │
│   │               │   [ PLAY ] [ VOLUME ]   │
│   └───────────────┘                         │
│                                             │
│   [ RECENTLY PLAYED ]                       │
│                                             │
└─────────────────────────────────────────────┘

The album artwork acts as both information and interface.

On desktop, the cover and track information can sit next to each other.

On a smaller screen, the player stacks vertically.

The controls remain large enough to use without requiring precision surgery.

The visual design remains fairly simple because a radio player does not need seventeen animated states.

It needs:

  • A visible play button
  • A visible stop or pause state
  • A volume control
  • The current artist and track
  • Artwork when available
  • A useful error message when something fails

This sounds obvious.

A surprising amount of modern interface design appears to have forgotten it.


Now-Playing Data

AzuraCast exposes public now-playing information as JSON.

The website requests that information and receives data about the current track, including the artist, title and artwork.

The simplified process looks like this:

Browser
   |
   | GET /api/nowplaying/1
   v
AzuraCast
   |
   | JSON response
   v
JavaScript
   |
   | updates page
   v
Artist, title and artwork

The JavaScript does not need to rebuild the page.

It only changes the specific elements that contain the current track information.

This is one of the useful things about the older web model.

The document exists first.

JavaScript improves it.

The complete page does not need to be a JavaScript application merely because three lines of text change every few minutes.


The Album-Art Problem

The now-playing data is easy to fetch.

Getting everything to change at the correct moment is less easy.

The audio stream, metadata and artwork do not always update simultaneously.

The track title may change while the browser is still showing the previous cover.

The browser may also decide that it has already downloaded that image and there is no reason to request it again.

The result is a new track title displayed beneath old artwork.

The current approach includes:

  • Checking whether the track identifier has changed
  • Adding a cache-busting value to artwork URLs
  • Preloading artwork before replacing the current image
  • Updating only when new information is available
  • Polling the API at a reasonable interval
  • Keeping the previous artwork when the new image fails

I do not want the browser requesting the complete now-playing response every second.

There is a difference between live information and attacking your own server.


Recently Played

The track history used to be a separate embedded view.

I am now integrating it into the player as a drawer.

The listener can open the history without leaving the page or losing the main controls.

The difficult part is not displaying a list.

The difficult part is displaying the list without the rest of the player jumping around.

The player needs a stable outer size.

The history should scroll inside its own section.

Long titles need to wrap or be shortened properly.

Closing the history needs to return the interface to the same state.

On mobile, the drawer needs to remain usable without occupying the complete page.

These are small interface problems, but personal websites are mostly made from small problems arranged close together.


Rebuilding the Schedule

AzuraCast already stores the station schedule.

The website does not need another scheduling system. It only needs to present the existing information better.

The revised schedule is divided into three levels.

Now and next

The smallest block shows the program currently running and what follows it.

NOW
All Ages Punk
18:00–20:00

NEXT
Crust and D-Beat
20:00–22:00

This version can appear close to the player without taking over the page.

Today’s schedule

The daily view displays the active and upcoming programme slots.

The current programme is highlighted.

The remaining programmes are presented as a simple vertical list with clear start and end times.

The main requirements are:

  • Six upcoming programme slots
  • A visible active programme
  • Clear time information
  • Clean playlist names
  • Automatic updates
  • Readable behaviour on mobile
  • Useful empty states when nothing is scheduled

Weekly navigation

The weekly view does not attempt to place seven full columns on a phone.

Instead, it provides a row of selectable days and displays one day at a time.

[ MON ] [ TUE ] [ WED ] [ THU ] [ FRI ] [ SAT ] [ SUN ]

18:00  All Ages Punk
20:00  Crust and D-Beat
22:00  Experimental Noise

This does not show the entire week simultaneously, but it is readable.

Sometimes showing less information is better than making all the information technically available and practically unusable.


Time Zones

The station schedule follows Athens time.

The listener may not be in Athens.

I currently spend much of my time moving around, so the problem became obvious while looking at my own site and having to remember which time zone it was displaying.

The station time should remain the official reference.

The browser can optionally display the listener’s local equivalent.

Station time: 20:00 Europe/Athens
Your time:    19:00 Europe/Stockholm

This conversion can happen locally in JavaScript.

The site does not need geolocation.

It does not need to store the listener’s location.

It does not need to ask a tracking company what country the visitor is in.

The browser already has a time zone.

Use the information that exists and do not collect the information that is not required.


Organising Hugo Content

The revamp is also changing how posts are organised.

AthensNoise.Rocks publishes different kinds of material:

  • News
  • Interviews
  • Reviews
  • Essays
  • Album streams
  • Mixtapes
  • Radio updates
  • General site posts

Previously, most new content could appear in the same latest-post list.

That is technically correct.

It is not always useful.

An album stream may belong in the album-stream section without taking the main position on the homepage.

A short technical update may need its own category but not the same prominence as a long interview.

Hugo already provides sections, categories, tags and custom front-matter values.

There is no need to install another content system.

A post can include a parameter such as:

title: "Album Title"
date: 2026-07-26

categories:
  - "Album Streams"

tags:
  - "Punk"
  - "Greece"

showInLatest: false
featured: false

The homepage template can then ignore posts where showInLatest is set to false.

{{ range where site.RegularPages "Params.showInLatest" "ne" false }}
  {{ partial "post-summary.html" . }}
{{ end }}

This gives each post an editorial destination without changing the underlying publishing workflow.

The post is still a text file.

The archive remains portable.

The site does not need a dashboard full of switches when two Boolean values will do the job.


Podcasts

AzuraCast also provides podcast feeds.

The problem is that an RSS feed is data, not a complete interface.

The website still needs to:

  • Request the feed
  • Parse the XML
  • List the episodes
  • Display descriptions
  • Display artwork
  • Provide an audio control
  • Handle missing information
  • Handle errors

Browsers may block the request when the feed does not provide the correct cross-origin headers.

This is one of those situations where every individual component is working correctly while the complete feature still does not work.

The browser can open the XML.

The server can provide the XML.

The JavaScript can parse XML.

The browser security model then steps in and informs everyone that they are not allowed to speak.

The possible solutions are:

  1. Configure the correct headers.
  2. Use a small server-side proxy.
  3. Download and process the feed during the Hugo build.
  4. Generate a local JSON file for the frontend.

The third or fourth option may be the most suitable for a static site.

Not every piece of information needs to be fetched live.

Podcast episodes do not change every ten seconds.


Mixtapes and Video

The mixtape section needs a clearer archive and a more reliable player.

The recordings should be treated as part of the station’s history rather than random audio files attached to old posts.

Each item should have:

  • Artwork
  • A title
  • A description
  • A date
  • Audio controls
  • Links to related posts or contributors

The video gallery is also being simplified.

Large third-party widgets often provide many features but also introduce:

  • Additional scripts
  • Tracking
  • Branding
  • Layout problems
  • Slow loading
  • Features I did not request

A small responsive gallery can do less and work better.

That is another idea I took from the boring internet.

A website does not need to become a platform.

Sometimes it can simply be a collection of pages with useful links.


IRC Is Still There

AthensNoise.Rocks has an IRC channel on OFTC.

IRC is old, simple and still useful.

It does not need a phone number.

It does not need an account connected to an advertising identity.

It does not insist that every conversation become content for a recommendation system.

The browser cannot connect directly to a normal IRC server because browsers do not provide raw TCP connections.

A WebSocket gateway is required.

Browser
   |
   | WebSocket
   v
IRC web gateway
   |
   | IRC over TLS
   v
OFTC
   |
   v
#athensnoise.rocks

Running my own gateway is possible.

It would also become another service that needs updates, monitoring, backups and troubleshooting.

For the moment, using an existing hosted IRC client is probably the sensible choice.

Self-hosting is valuable because it provides control.

Self-hosting every available component without considering the maintenance cost is how a hobby turns into unpaid systems administration.


The Visual Direction

I am a developer, but I started as a graphic designer.

This affects how I approach the rebuild.

I am not interested in making AthensNoise.Rocks look like a generic streaming application.

The site should remain rough, readable and recognisable.

The main colours are still close to:

:root {
  --background: #2e3436;
  --accent: #009999;
  --text: #fef6d4;
}

The interface should feel closer to a small publication, zine or community radio site than a software product.

The design goals are:

  • Dark but readable
  • Rough but functional
  • Consistent across the site
  • Responsive
  • Lightweight
  • Accessible enough to operate without guessing
  • Independent from the current design fashion

A large heading does not need to occupy the complete screen.

Every section does not need to be a rounded card.

Every button does not need to glow.

AthensNoise.Rocks is not trying to optimise listener conversion.

It is trying to play music.


HTML Is Already a Design System

One thing I appreciate more as I get older is that HTML already contains a design system.

It has headings.

It has paragraphs.

It has lists.

It has links.

It has quotations.

It has tables.

It has forms.

It has buttons.

A browser can display all of these without downloading a framework.

CSS can improve their appearance.

JavaScript can improve their behaviour.

But the basic document should remain understandable without either one.

<article>
  <h1>Programme title</h1>

  <p>A description of the programme.</p>

  <audio controls src="/stream"></audio>

  <a href="/schedule/">View the schedule</a>
</article>

That is already a functioning interface.

The more complex version should be built on top of it, not replace it with an empty <div> waiting for several megabytes of JavaScript to decide whether the listener is allowed to see a play button.

I am not against modern development.

I am against using a forklift to move a chair.


Contributors

The contributors page is also being redesigned.

AthensNoise.Rocks should not look like an anonymous automatic playlist.

People select the music, write the posts and contribute material.

The new contributor layout will include:

  • Name
  • Photograph or avatar
  • Short biography
  • Musical interests
  • Role in the project
  • Links to related work

On wider screens, the profiles will alternate between image and text columns.

On mobile, they will collapse into a single column.

The site is independent, but it is not impersonal.

The people behind it are part of the design.


Privacy and Project Information

The privacy policy, terms and radio licence are being reviewed as part of the rebuild.

These pages are not exciting, but they should accurately describe the project.

The principles remain simple:

  • Do not collect data without a reason.
  • Do not add analytics merely because they are available.
  • Explain external embeds.
  • Avoid unnecessary cookies.
  • Keep location and time-zone conversion inside the browser where possible.
  • Do not pretend the project is a large commercial service.
  • Be honest about how the site operates.

AthensNoise.Rocks is a non-commercial personal and artistic project.

It exists to publish writing, play music and support independent culture.

It does not need to know the listener’s age, income, shopping habits and favorite brand of toothpaste.


Deployment

The deployment process is still intentionally basic.

Hugo generates the site into a public directory.

A script copies the generated files to the server.

Content files
     |
     v
hugo
     |
     v
public/
     |
     v
rsync over SSH
     |
     v
Web server

This is not continuous deployment with a complicated pipeline.

It is a shell command.

That is enough for the current project.

A simple deployment system has several benefits:

  • It is visible.
  • It is easy to reproduce.
  • It is easy to debug.
  • It does not depend on a third-party platform.
  • It can be replaced without rebuilding the site.

The important part is documenting it properly so I do not have to rediscover my own process later.

Documentation is generally a letter written by your present self to a future, more irritated version of yourself.


What Is Working

The foundation remains stable.

The following parts already exist or are in active testing:

  • The AzuraCast station
  • The Icecast stream
  • AutoDJ and playlists
  • The Hugo website
  • The now-playing API
  • Track metadata and artwork
  • Schedule data
  • The custom-player prototypes
  • The daily schedule
  • The article and interview archive
  • Hugo categories and tags
  • The deployment scripts

The problem is no longer creating every individual component.

The problem is integration.

The parts need to share one visual language and one understandable navigation structure.


What Still Needs Work

Player

  • Improve artwork timing
  • Improve error messages
  • Finalise mobile behaviour
  • Test playback across browsers
  • Keep the player stable when history opens
  • Remove old player experiments

Schedule

  • Finish seven-day navigation
  • Improve time-zone conversion
  • Handle empty programme periods
  • Refresh active programmes automatically
  • Reduce unnecessary API requests

Hugo

  • Separate album streams from the general latest-post feed
  • Standardise front matter
  • Clean up categories and tags
  • Improve archive pages
  • Remove unused templates

Media

  • Fix podcast-feed loading
  • Complete the mixtape archive
  • Replace the video widget
  • Improve popup-player behaviour

Community

  • Finish contributor profiles
  • Improve the contact page
  • Reconnect the IRC interface
  • Organise external links

Infrastructure

  • Audit the CSS
  • Remove duplicate JavaScript
  • Document deployment
  • Review caching
  • Test backups
  • Test restoring the backups

A backup that has never been restored is a theory.


Things I Have Learned Again

Simple systems still become complicated

The original architecture remains simple:

  • One radio server
  • One website
  • One static-site generator
  • A few APIs

The complexity comes from the connections between those systems.

A current-track display involves metadata, artwork, timing, caching, JavaScript and browser behaviour.

A schedule involves programme data, dates, time zones, responsive layout and automatic updates.

Small features become systems when they interact with other small features.

Static sites can do a lot

A static website can still provide live information.

It can fetch APIs.

It can play audio.

It can display schedules.

It can convert times.

It can parse generated data.

It does not need to become a database-backed application to do any of this.

The browser is capable

Modern development often begins by replacing the browser’s native behaviour.

I am trying to begin from the opposite direction.

Use links as links.

Use buttons as buttons.

Use audio controls for audio.

Use headings to describe structure.

Add custom behaviour only when it improves the experience.

Old does not mean obsolete

HTML is old.

IRC is old.

RSS is old.

Static files are old.

Internet radio is old.

They still work.

In many cases, they continue working specifically because they are based on open protocols rather than the business model of one company.

Personal websites should be personal

There is no reason for AthensNoise.Rocks to look like every other music website.

There is no product manager requesting engagement metrics.

There is no investor asking for growth.

There is no algorithm deciding which page deserves visibility.

That freedom is the point.


The Actual Web

The actual web is not only the five applications most people open every day.

It is also:

  • Personal blogs
  • Small forums
  • IRC channels
  • RSS feeds
  • Internet radio stations
  • Hand-written pages
  • Static archives
  • Strange directories
  • Abandoned experiments
  • Websites maintained by one person
  • Websites visited by twelve people
  • Links from one person’s page to another

The internet does not need every project to become large.

AthensNoise.Rocks does not need millions of listeners to justify existing.

CrustyDev.com does not need to become a publishing platform.

A page can exist because somebody wanted to write it.

A radio can exist because somebody wanted to play records.

A link can exist because one person found another person’s page interesting.

That was enough before the commercial platforms and it remains enough after them.


Closing

The first AthensNoise.Rocks development diary was about getting the radio online.

This second phase is about organising what grew around it.

The stream still comes from AzuraCast.

The website is still generated by Hugo.

The content is still stored in text files.

The deployment is still mostly a shell script.

The system has become larger, but its basic structure remains understandable.

That matters to me more now than it did when I started.

I do not want to build something that only works while a particular platform, framework or service continues to exist.

I want a website made from files that can be moved.

I want a radio based on open streaming software.

I want pages that can be linked directly.

I want the content to remain readable without an account.

This may be the boring internet.

It is also the useful internet.

The rebuild is not finished.

The player still has problems.

The podcast feed still occasionally behaves like XML has personally taken offence at me.

The CSS contains decisions made by several previous versions of myself, none of whom left useful documentation.

But the radio is still streaming.

The pages are still loading.

The links still work.

For now, that is a good base to continue from.

Keep the server small.
Keep the files readable.
Keep the links working.
Keep the radio playing.
Keep making noise.