Hacker News new | past | comments | ask | show | jobs | submit login
Making a music library without a SPA (begin.com)
245 points by todsacerdoti 9 months ago | hide | past | favorite | 95 comments



Interesting exercise. It explores the upper bounds of what can be done this way and proves that forms and links don’t need to be an SPA monstrosity.

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.

0 - https://astro-records.pages.dev/


Can we stop using expressions such as “SPA monstrosity” when this “unbloated HMTL” website breaks copying links, the back button, the address bar, don’t cache ressources properly, etc etc


Give it a couple of years, people will bemoan the "Frankenstein" of partial server rendering, pine for the "consistency" of SPAs instead.


The Astro version feels just like a SPA to me.


consider it browserwreck feature complete /s


I agree. They did a very cool exercise. But let be honest here, most of the users today would not put up with a UX like that. Even for a tech person who is used to some very hostile UX day in and day out, I would still not use that if I have the option.


The Astro one is indeed not a SPA. But the original one is a SPA composed of multiple iframes. It is like old AJAX hacks.


Those albums sound a bit different! :)


To me this article and demo shows why we use SPA's, and how much modern frameworks actual do for you. It's hard to not see a whole lot of very basic problems in this demo that any modern framework would solve out of the box.

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.


When I started in the industry, progressive enhancement as a concept was the theme du jour. As SPAs have gained prominence, I’ve sadly watched the drive to improve them and sometimes pushback against them morph this concept as it’s taught to newcomers into “No JS” or “low JS” as a side effect. It lacks nuance.

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...


At first, I thought it was a good exercise (and it still is), but going through the result [0] made me more skeptical.

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?

[0] : https://enhance-music.com


Looks like the enhance-styles.css doesn't get cached properly and gets requested on every route. The browser then waits for 500ms for a response from a server, likely due to increased web traffic.

An issue which could have been avoided by using a SPA :D


So the solution to cache a resource it's to use an SPA? why not find the reason for this css not being cached and solve it?


This is what I got going from the main page to an album page:

55ms for html, 67ms for css, 15ms for webp image.

I'm in bay area so it might be slower in other places


Runs slow, not loads slow. Also, what bay area. There must be thousands of bay areas on a planet covered by 2/3 water


What planet? There’s billions out there too


Could be the cargo bay area of a huge alien star ship...

(Btw from my own bay area of a certain SE Australian state it does feel a little laggy when navigating, but quite usable)


It’s a bit US (and tech) centric, but Bay Area is short for the San Francisco Bay Area.


This is where upper case actually makes a difference. Maybe that’s what the parent was alluding to.


The one associated with this website.


I agree with other commenter, works fast for me.


Yep, the site is very slow in Southeast Asia.


Can you expand on what you mean by slow? For me everything loaded extremely fast.


the main page loads fast but the interactions are slow, like there's some artificial delay

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


Very fast for me.


Am I getting old? As far as I can tell, this is what we were doing before JS became the mighty weapon it is today (like pre-2005).

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.


If you look at things like htmx and the principles that underpin that many would say the past 15 years have been a holding pattern waiting for browser APIs to catch up and provide a decent front end experience.

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.


Then one might even use jQuery here and there. A bit more convenient than vanilla JS.


We not only want to in name of performance, the new folks are rediscovering gunpowder as we say back home, adding SSG and SSR to SPA frameworks.


Not with this quality nor was it tried with modern tech. New times, new ways to do the same thing. I would prefer to code using this Enhance framework used by to develop this app than React.


The problem I see here is that it's not linkable - I can't see my current location in the url bar, which means I can't share it to other people. Maybe you could fix this with some JS?

Cool idea though.


Zero JS makes sense in some cases but this seems more code golfing than a real product that users would love.

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)


There's a huge gap between some JS and Spas though. The question for me is always where data is rendered to markup, not whether it uses JS in the browser at all.


> There's a huge gap between some JS and Spas though

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).


For me the difference isn't the amount of JS, it's what the JS is doing. A page that requests JSON and renders it to HTML in the frontend is fundamentally different from one that uses JS for basic interactivity.

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.


> With our ‘zero JavaScript’ happy paths now covered, we can have a little more fun when JavaScript is available in the browser.


JS is available in the browser unless someone knowingly disables it. It is a standard.


JS will fail to execute a lot more often than most people realise, e.g. due to poor network conditions, particularly if hosted on a different origin (first-party JS is far more reliable, and inline JS the most reliable).


> e.g. due to poor network conditions

Do you have any metrics on this? Seems like an extremely rare edge case.

Anecdotally, I haven't experienced it in years.


The (semi-old) UK stats said 1.1% failure rate, but somebody more recent said it had increased to 3%.

That's a lot.


Somebody said is a bad source. 3% failure seems too high given nearly everything requires JS.


"Everything requires this" does not mean "this is always available"!


> 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)

It’s there when you need it? Using less bandwidth and cpu means using less battery.


Everything comes at a cost, in this case it was deep linking. Even though back and forward buttons work, navigating to an album won't update the URL which won't allow sharing and will be bad for search engines since all navigation points to the same URL.


That's fairly easy to fix (in principle; I don't know what their backend is; it might require JS).

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.


I built a podcast library/player[1] using HTMX and Alpine, using Django as the backend. It has an audio player that would stay open and keep playing while the user navigates around the page. This is quite easy to do with HTMX.

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.

[1] https://github.com/danjac/radiofeed-app


I have to ask, partly because I went around this same circle in my head when evaluating htmx and deciding that it was cool enough but I didn't want every interaction to be a server round trip.

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.


Alpine is just used for pure client lightweight interactions, such as toggling a menu (if CSS was insufficient) or (in this case) handling the audio player controls. Of course, I could even go more lightweight, and just use vanilla JS in such cases, but as both libraries are pretty small anyway it was easier to manage client DOM interactions with Alpine.

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.


Thanks, that was helpful and got me thinking.

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.


It stops playing when I press the back button. I dunno if that’s really working with the web platform.


Lol is this really what you want to show the world?

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.


Just a side note: It bothers me a bit that most audio player interfaces make it unnecessarily difficult to navigate within a title. This here is a good example: The waveform extends across the entire width of the window and could be wonderfully used to jump precisely to the desired spot (Soundcloud does this comparatively well). Instead, I only have an extremely narrow bar here, where a few pixels of movement mean a jump of several seconds.


It’s also common to see a seek bar that is too thin and hard to click.

This one is 19px tall: https://chiptune.app


We're again swinging to the extremes.


Love to see this, and hope that things like it become more practical as the HTML standard develops further. While there’s nothing wrong with JS in itself, the less that’s required the better from several angles — the big ones for me are less required dev work, less need for miles-long lists of dependencies, and better likelihood that the various components of the app are implemented consistently well since they’re the responsibility of web engine maintainers instead of random library devs.

For me personally web front end as a platform becomes progressively more interesting as HTML includes more robust widgets and functionality.


Devil’s advocacy here (and I know this is overwrought and beaten to absolute death here and abroad daily) but is a large dependency tree really that bad?

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.


I’m not an HTML purist, JS is fine, but I prefer to use it only where it’s necessary.

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.


Large dependency trees are usually not about using docker or not. That is rarely a dependency of the code itself. The complaint about large dependency trees is about hundreds or even thousands of dependencies of the code itself, where you could get away with maybe tens, if you write trivial code (left pad ...) yourself and avoided needlessly adding more stuff on top. Every time one adds a dependency, one should feel a little sting and make an uncomfortable face and remember, that this dependency might make things badly maintainable, difficult to upgrade, or vulnerable. Always ask oneself things like: "Did I read the code of that dependency and its dependencies? Do I really know what this code does?" and of course if one does now lock down that dependency to exactly one version, one would have to consider these things every time one upgrades dependencies.

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.


It is true that JS and the web + JS ecosystem is absolutely overloaded with “utility” dependencies like lodash or some weird fusion experiment in a way that Python or Java is not. I personally have implemented and removed a ton of dependencies in the latter two languages with little hassle - I’m aided by the fact that these languages support DI or hexagonal architecture and a good project layout can prevent something that I don’t need, care about, or understand from becoming ubiquitous and cumbersome as my codebase grows.


This is interesting as a demo, but I'm curious how would this solution handle chunking up songs for better performance (think long podcast episodes or higher quality audio), or a DRM like Widevine.

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.


Very opinionated, but the end result is beautiful. Looks like Apple Music.


In the source code I see a lot of JavaScript, usage of service workers (the tech behind web apps) and a not great general UX, besides an herculean work to make it work for 1% of the users in the world that navigate the web without JS enabled.

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.


It doesn't have a volume control, I really can't understand the point of these "minimal UIs" that don't have the very most basic needed _feature_.


That's not a problem with the whole concept, it's just a problem with how they did it. Volume controls are built in to the native music player element


Yep, that's right. The "mini player" on Windows has exactly the same problem. Its a big box of 200x100~ pixels or so and it doesnt include a volume control, you have to exit the mini player. I do not understand the reasoning of such decisions, really. It maybe looks cute but It's pretty useless.


Shouldn't that be handled by the native platform?

I have a thinkpad from 2008 with dedicated volume keys...


No, because I could be playing a game with its own volume, while also being in a webrtc call with someone on another tab. Volume control for a music player is no-brainer.


Not so long ago I had this problem on bandcamp too, but they do not attempt to run without JS at all. I regulated volume for the website in my OS instead. However, when it switches to the next track, it would blast off my ears again. This issue is not limited to websites like the one linked here.


Removing JavaScript from your frontend means you have one less application, build system, dependencies and oncall schedule to maintain. Love to the authors, wonderful job.


You can just remove the build system and most dependencies from a JS app. Modern JS is great if you are ok to serve 85-90% of world audience.


I remember back in the 90s, web pages could have persistent music by using a frameset. Put the player in one frame, and the browser in the other frame.

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!


the biggest problem, at least for me, is that it breaks links - you can only link to the frameset, not the actual page, and the url doesn't change on navigation.

unless you start doing javascript address bar manipulations, in which case you've got both an SPA and frames.


Is this really that different to javascript applications where the state does not alter the URL? Which seems to be most of them.


What’s the issue with using JS? Seems like the perfect tool for the job here.


And now imagine, that this approach was hyped to no end and suddenly everyone was building every web_app_ this way, maybe in some fancy framework for it ... is basically what happens in frontend land but the other way around, web_sites_ being build as SPAs.


TL;DR: They used an iframe for the content.

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.


My only concern is not being able to share the state of the page with others.

I feel like that is a necessity.


Correction: this is a SPA. Only even more over engineered than a nextjs or gatsby one.


SPA = Single Page Application


I browse the the web on desktop with JavaScript disabled by default. The demo works. I don’t even need to know what enhancement are added with JavaScript. The basic HTML version is perfectly fine.


damn, this is fucking awesome:

https://enhance-music.com

you can disable 3rd-party, 1st-party and even inline JavaScript, and it keeps working.


I feel uneasy when everything goes blank on click and the back button doesn't get my a page higher in irder, but the previous song. :(


What i love about JS and AJAX is it "fixed" the "double submit" problem in pure MPA :))


Absolutely beautiful and a relief of fresh of air from the dumpster fire that is SPAs.


Nice write up and a refreshing take on building websites w/o relying too much on JS! Great experimental music too.

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!


Wait, can you just upload arbitrary files to SoundCloud? Is there no copyright check?


I was talking about the "music" I make, so I don't know.


Now this looks greatx but xhe main reason I have been sticking to SPA frameworks is to persist and dynamically swap components like SiteMaps, Table of Contents, Cart lookups and chat windows. Is a similiar solution, based on iFrames going to replace this for me?


May as well go back to the original frames


Going through the docs, nothing there about how to edit state?


Where are my HTMX people?


People tend to blame SPAs/JavaScript (instead of the people holding them) for breaking the address bar, the back button and the ability to open links in a new tab, but this does all three of those.


Yeah, I just tried it and it's horrible!

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.


Does it? Or is playing another track conceptually a new page?


IMO a new page is when you explicitly click something and most of the page changes




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: