Making a music library without a SPA
begin.com
begin.com
But comparing this to the linked Astro version [0], Astro wins hands down. It feels faster, URL works, and behaves more predictably.
Turns out a music player is best built using SPA-y tech. And it is possible not to build a monstrosity along the way. These so-called island architectures are right on the money. It is server first, but breaking out of it and building some client side interaction is seamless when required.
The "SPA bad" arguments are also largely outdated. Most apps that are build now are at least hybrid, with some SSR getting best of both worlds, urls and navigation are solved, accessibility is improving with better component libraries linters and best practices, automatic bundle splitting, highly optimized caching of static assets, build tools with hot reloading as a standard option, static exports, etc etc.
Getting a good Lighthouse score is not the flex it used to be (referring to the enhance movies demo), my all-JS Next.js app scores 100's without even trying.
The only potential argument would be users not having JS enabled, which is not relevant to most apps and use cases. In most cases you do not want to degrade the experience of most (but more likely all) of your users, and with SSR you can have it all.
Obviously build for your audience and use cases and apply technology that makes sense and I welcome experimentation with alternative approaches, but saying "spa bad" is imo not very productive.
For example, your menus should work without JS but that doesn’t mean they should work as well as or exactly the same way as they do with JS disabled. There are many features and patterns that a11y/screenreader users (which I recognize I’m somewhat lumping in with the “I disable JS for X reason(s) crowd here) appreciate that are still impossible without JS. I’ve surprised people on a couple occasions by showing them how much JS is actually used to create many of the WAI-ARIA menu proof of concepts[0], for example.
I think the “a11y is easy” crowd, well intentioned as it may be, is also partially to blame for this. I’ve read articles with titles like “How to do X with CSS only” that gloss over many UX downsides to the chosen approach.
The sad reality is that a11y users represent another audience with a different set of formed expectations one needs to cater to, and fulfilling those expectations is not free more often than some would care to admit, especially when you consider how complex the differences in approaches between screen readers can be. This isn’t an excuse to not help those users, but I have wondered before if we would be doing better if we were more scathing in our commentary on vendor standards as it relates to this. Who knows? Perhaps I’m naive.
[0] https://www.w3.org/WAI/ARIA/apg/patterns/menubar/examples/me...
It is... slow? I mean, Internet Explorer slow. Maybe I'm spoiled by the level of responsiveness of application-style web interfaces, but opening an album or returning to the library feels slow. Is it because I'm browsing from Western Europe and the application is hosted in the USA?
I'm used to browsing multi-page apps that don't pretend to be apps, and having a 500ms load time after a click is expected and feels right. But waiting the same time for a click in a page that looks like an app makes me uncomfortable. It's weird - is this the Uncanny Valley again?
the 500ms estimate above seems about right... it should be much faster. Navigating from one static page to another should be sub-100ms assuming the server is on the same continent
55ms for html, 67ms for css, 15ms for webp image.
I'm in bay area so it might be slower in other places
(Btw from my own bay area of a certain SE Australian state it does feel a little laggy when navigating, but quite usable)
An issue which could have been avoided by using a SPA :D
So if you don't like JS, this is a pattern you might want to revive, but I can hardly believe, that we want to go back to that world as a standard for web development.
Personally I love working on in an “older” style way here the server just sends html to the browser and we don’t need to built an app twice (once in the fronted and once in the back end).
The real magic comes in having the older, full stack approach, sprinkling in JS as needed and then having modern CI/CD for deployments.
Cool idea though.
I totally agree bloated SPAs are terrible and in most cases an MPA is the best option. But a bit of JS is certainly fine when it makes for a better experience. Even HN uses a bit of JS.
And what's the point of having all this bandwidth and CPU power available if we're not going to use it? (even one tiny bit)
Do you have any metrics on this? Seems like an extremely rare edge case.
Anecdotally, I haven't experienced it in years.
That's a lot.
I tend to agree but it depends.
I made a small SPA a coupe of years ago with Mithril and the whole thing is like 40kB gzipped (JS and CSS).
Both uses absolutely have their place and there are products that would be noticeably worse for the end user if all HTML was rendered on the server. I've just learned to have a high bar there, the complexity grows quickly making bugs more likely and debugging more difficult.
It’s there when you need it? Using less bandwidth and cpu means using less battery.
But there's so little value in the navigation, that I'm not surprised it works as an "MPA" (even though it feels janky). And that's all not done by JavaScript. The player, the animation, etc. still requires it. TBH, I don't see the point of this demonstration.
It's pretty simple - it's just for my own needs - but it works quite well. You can go far these days before needing an SPA.
What do you get from combining htmx with alpine that you wouldn't get more simply from just alpine?
The way I saw it was that I'd end up with some state in the client, some on the server, some in json, some in html, my server would have to know how to render some components, the client others.. and for what benefit? Alpine can fairly easily do everything htmx does and it's one less framework to send to the user, one less thing independently watching and acting on the dom, one less dependency to manage, etc.
There's no JSON in this site: the only pure non-HTML interaction is a "ping" where the audio player posts the latest play time of an episode every few seconds while running, so that if the user switches to another device, or closes the tab, they get the latest runtime of the last episode they were listening to.
In terms of performance, I found there wasn't much difference between this approach and an SPA. For example, if I navigate from page A to page B in a React app, I still need to fetch JSON or GraphQL data for that new page, perhaps including multiple round trips to the server depending on the API design. With HTMX, I can just fetch a small HTML snippet: for example, if I click a "Subscribe" button, it just returns some new HTML showing "Unsubscribe" and updated URL or whatever. Sure you can do that with plain Alpine, but HTMX makes it easier to reason about state, which I'd need to otherwise manage client side (e.g. keeping a tally of all my subscribed podcasts).
HTMX also provides solutions for things like updating state in multiple places: for example you if you have a shopping cart, and you want to update not only the cart itself but a counter widget in the navbar, you can do "out of band" responses that can insert snippets of content outside the main target. Again, totally doable with Alpine, but more work for me.
And not every interaction has to be a server round-trip, you can certainly push work to the client side if needed, and use Alpine or vanilla JS or even React if you want: in a work project for example, we had most of the site in HTMX, but used React for a dashboard page with lots of complex interactive graphs and whatnot.
As I said, it was a pretty simple app, certainly not as complex as Spotify. But given that the often repeated example of when to use an SPA is "running an audio player while navigating around the site", it makes me wonder why the need to reach for React or other SPA framework for even simpler needs.
Indeed just using vanilla js was something that occurred to me, or drawing clear boundaries around where things happen which it sounds like you've done here. In the end I decided it wasn't going to be long before I wanted to do some basic sorting or filtering or something of nontrivial objects on the client and that was where my brain decided it was more effort than it was worth for whatever I was trying to prototype.
I've largely settled at the moment on alpine multipage apps with traditional json rest APIs behind them and some light serverside templating for the initial loads, and the only thing so far that makes me reach for something heavier is if there's a lot of user-defined content and I want to feel confident of being able to run under a strict CSP.
But it sounds like you landed on something that makes sense as well so thanks for the explanation.
If you're going to take this trite stance, what you demo has to be really good.. and it's really not. The first I see is really bad FOUC which is weird when this should be mostly static html. Interacting with the website is then super janky: there is a noticeable delay when using the music player, it seems to "lag" when playing, and clicking on albums flashes the cover full screen for a second and then shows me the page.
Lots of stuff like that. All in all, this is not the sort of thing I would want to put out there with this sort of message. I wouldn't want to hire a company that takes this sort of stance and then delivers garbage.
This one is 19px tall: https://chiptune.app
For me personally web front end as a platform becomes progressively more interesting as HTML includes more robust widgets and functionality.
Most modern backend development assumes a ton of dependency management, tech like Docker has proven for at least 10 years that it can be manageable in production at scale. While I get that browser compatibility can be a pain in a way that the backend doesn’t have to reckon with I don’t really see why dependency management for something like React should be a decision factor for front end.
That’s also not to insinuate that we should just do it (SPA + massive amounts of JS) because browsers can handle it or that there are zero downsides to JS dependency management, but rather that the positives and negatives of zero-JS solutions should be adjudicated on things like developer experience and speed of delivery.
The main thing that’s bad about massive dependency trees in my mind is that it makes them difficult to keep a mental map of, which makes for pain when things go awry. Most of the work I do is on iOS apps which is a generally more “batteries included” environment, and there most dependencies only go a level or two deep, which is reasonable to reason about and makes it more practical to track down problems. iOS apps also just don’t need as many dependencies in the first place.
It’s not practical for web front end to require as few dependencies as e.g. iOS, but I think it’s a worthwhile endeavor to push that line as far as it can go so it’s more practical to develop a complex low-dependency web app.
Now for a throwaway web app maybe one does not need to care. But then one should also label it as such. A throwaway, nothing to be taken as a good example. Definitely not production ready code.
Without these, the farthest you could go on this path is a unique way to show off some indie work.
I remember one of my favourite bands in my teenage years (think early 2000s) had a website where they let you listen to their albums. I wanted to listen to them on my mp3 player so we figured out we can just look up the URLs of the mp3s from the HTML and download it.
The code quality is great and enhance framework is top notch quality, I'm happy to see this as I'm building something similar ("build your entire app with accessible HTML") but without making 1% of the users the main selling point.
I have a thinkpad from 2008 with dedicated volume keys...
Frames were generally a bad idea, but I always wondered what exactly was wrong with this one. Seems like a reasonable idea, and you wouldn't even need Javascript!
unless you start doing javascript address bar manipulations, in which case you've got both an SPA and frames.
But this brings with it the navigation issues that were the reason we abandoned frames: The URL in location bar no longer reflects the location of the specific page, so it can't be bookmarked easily. Also, when opening in a link in a new tab, we get the reverse of this problem in that the other frames are lost.
You could solve this with JavaScript that updates the location, I suppose, but then the result is essentially an SPA.
I feel like that is a necessity.
you can disable 3rd-party, 1st-party and even inline JavaScript, and it keeps working.
I've run out of free space on soundcloud, and Bandcamp is incredibly slow to upload files. So I might as well just fork this project and host the audio library myself. Kudos Cole!
I'm all for making things work without JS, but demonstrating that my experience can be equally bad with plain HTML+CSS doesn't feel like an improvement.
Anyway we can have SPAs with a working back button and a working address bar with sufficient competence and motivation. Links in a new tab would work if all the state was in the URL, but messy URLs are unfashionable these days.