The Internet radio stream player hosted by this blog has been completely overhauled, revamped, refactored, rewritten. In fact, it’s literally by no means what it was, even if it looks almost identical. Here’s its story.

“It’s for your own security” (I guess)

After IONOS updated my PHP to 8.3.36 in response to PHP2Shell, I noticed that I could no longer update the list of Internet streams in my main radio player! I had no issues editing and saving the changes for the secondary ones, which have 1 and 28 streams, respectively—but the main player, with 500+ streams, was frozen forever. I couldn’t find anything in the .htaccess file, but IONOS might have tightened the security somewhere, and the update code received a “400 Bad Request” on large POST payloads.

My first approach was to ask Gemini 3.8 Flash (High) to write an update.php for me. The idea was to export the player data (names, streams, thumbnail URLs) into a JSON file that I could edit, then upload it and have it pushed into MariaDB, that whore of a database.

It worked. But then, as long as the whole player-editing GUI that the plugin offered me through WordPress’ Dashboard was useless (except for small lists of streams), why should I have kept using a WordPress plugin? It’s heavy, it loads too much CSS and JS it doesn’t use, and it queries a DB when each player can be a JSON file staying on the disk! No more DB queries—just PHP and JS, plus Apache.

Once again, it worked! I was so pissed off when I learned that my ultracheap, telco-sponsored Gemini AI Pro subscription is only “half-Pro” now: Claude Sonnet and Opus will become unavailable starting Nov. 2 (and the current weekly quota is so pathetic that it expired in less than 9 minutes of Opus 4.6 + 8 minutes of Sonnet 4.6); when used for coding, Gemini 3.1 Pro is only available to Enterprise plans, and Gemini 4 Argon works in the Antigravity App, Antigravity CLI, and “IDE extensions” but not in Antigravity IDE, and not for “half-Pro” subscriptions, anyway! So I was left with Gemini 3.8 Flash, a model I had reasons not to trust!

The birth of the new player

Surprisingly, Gemini 3.8 Flash performed satisfactorily. I had to interact with it a lot, because LLMs and common sense don’t belong to the same universe. I even wonder how they are able to write working code in the end, when the helper Python code they write for tests often has blatant syntax errors that they need to correct! Reading the entirety of their thinking and intermediary output is fascinating. It’s surreal. But again, even Claude Sonnet can be extremely dumb at times, significantly dumber than Opus. In the end, I’d say that Gemini 3.8 Flash is noticeably better than Qwen 3.7 Plus. That’s for coding, because, as a general chatbot, Qwen seems to be slightly more trustworthy than Flash models; even so, for more complex questions, I ask Qwen 3.8 Max or 3.7 Max, not 3.7 Plus.

I appreciated that 3.8 Flash compacted the session when needed, without my intervention. Qwen Plus models tend to become confused when the context is filling up, but they’re not aware of their confusion.

This doesn’t mean that 3.8 Flash has common sense—it does not. While reimplementing the reading of ID3v2 stream data (something I partially fixed in the old plugin a couple of days ago), it came with strange solutions, including the caching of such “Artist – Track” info into in-RAM MariaDB tables! Vibecoding is mesmerizing, but you need to keep the steering wheel firm!

The task wasn’t just to remove the code’s dependency on WordPress, remove the GUI editing, and disconnect from the DB. It literally meant reimplementing almost everything! The original plugin had 12 themes, but I always used only one, slightly customized. So even on the CSS front, there was a huge cleanup—but I wanted to keep the UX almost unchanged.

FilesOriginal pluginCurrent solution
PHP1632
JS84
CSS171

And the DB access is no longer required, being replaced with one JSON file for each player! (Of course, I have to manually edit a JSON file, but this is actually much more straightforward than that bloody GUI.)

Reimplementing this radio player with Gemini 3.8 Flash (High) took 15% of my weekly quota (from 99.47% to 84.38%):

Some UI changes and improvements:

  • The list of streams now takes the full vertical size of the browser, adapting to both desktop and smartphone.
  • The volume slider is now horizontal.
  • The typeface changed from Bookerly to Bitter, which is much smaller.
  • The vertical scrollbar is now wider and always visible, also on Chromium-based browsers.

On the other hand, the newly introduced (thanks to Qwen 3.7 Plus) numbering of the list items is still meant for, and limited to, desktops, not for smartphones.

What changed for the user?

The old URLs are still working, but newer ones have been added:

PlayerStreamsOld URLNew URLAlso valid
Default512—ludditus.com/radioludditus.com/radio-taz
Ludditus’ Selection512ludditus.com/?radio_player=1ludditus.com/radio/1ludditus.com/radio-taz/?radio_player=1
China1ludditus.com/?radio_player=76ludditus.com/radio/76ludditus.com/radio-taz/?radio_player=76
ROMÂNIA28ludditus.com/?radio_player=79ludditus.com/radio/79ludditus.com/radio-taz/?radio_player=79

What didn’t—couldn’t!—change

Previously existing limitations that cannot be eliminated:

  • Many streams don’t include “now playing” ICY or ID3v2 metadata, and the official apps and websites use associated JSON or other means to display what’s currently playing. It’s frustrating not to have that info in this widget, but that’s how it is.
  • Some French radio streams refuse to play on a smartphone in a browser outside Western Europe, but have no problem playing in a browser on a computer regardless of where you are:
    • Radio Classique requires a Western European IP; the official app plays regardless of your location.
    • Chante France, M Radio, MBS, and Crooner Radio require a Western European IP even for their apps to play!
    • Radio Décibel refuses to play in a browser on a mobile device, even with an IP set to France, and it lacks an official app.

Screenshot mania (to make this page heavier…)

On PC: what’s displayed in Chrome, Edge, and twice in Firefox, with 125% desktop scaling, Windows:

If you really wanted a full browser width (100% desktop scaling, Firefox ESR, Linux):

With 125% scaling:

And a full Firefox window in KDE, also without scaling:

On Android: the main player and the one for Romania in Firefox; then, the main player in Brave, which seems to use a different DPI:

It works for me—I don’t know if it works for other people, or if they even care! 🙄