Hacker News new | past | comments | ask | show | jobs | submit login
Javascript, locking and sound (jwz.org)
74 points by AndrewDucker on July 25, 2015 | hide | past | favorite | 28 comments



This sounds like a good use case for a Shared Worker[0].

A shared worker is a web worker which can be shared by multiple pages / windows / tabs, and can easily be used for coordination of this type. The shared worker can listen for the asynchronous event and then send a message to an appropriate tab to play the sound as necessary. Unfortunately, Shared Workers aren't supported everywhere yet[1] (only on Chrome, Firefox, and Opera), but hopefully support will come to the other browsers soon.

Regarding playing sounds on iOS, the Web Audio API is a good way to go here. While it's true that you need a user gesture to trigger sound, you only need it for the first sound you play. So you can add a button to your web app which when clicked plays a short "empty" sound (just silence), and after that point you can play whatever sound you like at whatever time you like; it no longer needs a user gesture for it to be played.

[0]: https://developer.mozilla.org/en-US/docs/Web/API/SharedWorke...

[1]: http://caniuse.com/#feat=sharedworkers


As a developer I hated when I couldn't make my webpage play sound on ios. But as a user, it's the biggest relief in the world.

I hate autoplay, and if my phone had it, I would have to keep in on silent all-the-time, to stop from being rickrolled.


I agree but I wish they would have added a permissions dialog instead of outright blocking. If a site injects an autoplay audio tag iOS could prompt you once if that behaviour is OK or not for that particular domain. Apples creative interpretation of standards is pretty annoying to me as both a user and a developer, sometimes auto playing audio is actually wanted, in games for instance.


I dunno, I think the last thing anyone wants are more dialogs on websites, especially mobile.


Rant:

Moreover I think the whole "let the user decide" attitude is just a lame excuse developers hide behind when they can't figure out a decent UX. (It's no wonder design-by-committee software like we see on desktop Linux has so many UI options... Nobody can come up with anything people can agree on so "make it a checkbox so the user can decide" becomes the solution.)

Unsolicited sound is a horrible idea on any platform, it's a good thing that iOS only allows it when there's a touch event somewhere in the call stack. Dialogs would only worsen the problem; disallowing it altogether is the only sane solution.


A Belated Rubuttal Rant:

Developers do lean on users too much to solve UX problems, but I still maintain that giving users a choice here would have been the better alternative. Access to device sensors are almost always behind some kind of permissions dialog on major platforms. Personally I like web/iOS model best, it prompts me when an app/site wants to use my microphone, gps, camera, etc, and then remembers the choice I've made. Making potentially invasive or offensive application behaviour completely transparent to the user is a good user experience in my book, and it discourages developers from using those sensors unless they actually need them. Although I can concede that getting permission for unsolicited access to the phones speaker might be going to far from the small sample size here, and I am just as annoyed at the recent auto playing video trend on the web as anyone else.

Unsolicited sound is pretty important for games, and increasingly the web is becoming a pretty good platform for them. I wrote a little game a while back and I worked around the sound issue in Safari by initializing my sounds (just a few) on the users first tap, not a back breaking workaround or anything, but still annoying that I had to work around Safari. Here's the thing though, if I hadn't been testing on iOS a critical (the sound actually was) part of my game would have been broken on iOS. I guess I'm just grumpy about that... :-/


Pretty much agree with his general concerns, although IPC between web pages would probably bring some terrible security concerns, but this is a little off, IMO:

"Oh, and speaking of sound: on iOS, you flat-out cannot play sound unless somewhere higher up on the call stack is a touch gesture. Asynchronous sounds can't be done using the HTML5 audio tag, because... Apple are dicks, I guess?"

I'm fairly sure this is actually because I don't want a web page on my iPhone making sounds without going through notification preferences, and I'm the one that paid Apple, not the person who made the web page. ;)


although IPC between web pages would probably bring some terrible security concerns

Given that CORS and cross-document messaging have largely made the same-origin policy a mere formality, it wouldn't be unsurprising for this to be next.

The gist is a lot of people want to treat the browser like a universal operating system, and by definite it will be mangled into one by any means necessary.


The problem here is that localStorage/WebStorage already creates an IPC mechanism. You're able to create objects, and other pages in the same domain can read them. Add another object as flow control, and you have all the primitives you need. In fact, I'd argue that the current situation is more insecure, because it is an ill-defined backchannel instead of something formalized.

Regarding the audio issue, I think this hits up a greater issue. Browsers should have a permission dialogue for things like audio and video that work on a per-domain basis so you can control that behavior. Sometimes you want to have background audio from pages. Most of the time, it's annoying. Asking would fix that problem, even when the user initiates a touch event.


Sometimes rants like this are biting indictments of the state of things, but sometimes they're just a bit of laziness.

The web is a constantly evolving platform. There used to be many things that were impossible to achieve on the web; that number is less now than it used to be thanks to the addition of new APIs (LocalStorage among them), but it's not yet zero. In particular, we don't yet have an API to achieve what jwz wants to do. LocalStorage was clearly not designed with this use case in mind; acting surprised when your awful hack turns out to be unreliable seems a bit disingenuous.


His point is that these are pretty much Operating System 101 features that the browser doesn't supply since its intended purposes were quite orthogonal, though it has since painfully evolved into ostensibly usurping the host OS.


You don't need to poll. One can subscribe to storage events, and that way you can broadcast ordered messages to all your tabs without any polling.

https://w3c.github.io/webstorage/#the-storage-event


Wouldn't you still have to poll to check if the "leader" still exists?


If playing the sound isn't latency sensitive, you could just have each tab pick a random delay [1].

When the tabs receive the event, they each wait X milliseconds. The first timer to expire marks the event as "played". Other tabs receive this event, and abort their timers.

If a "leader" disappears, it will simply fall to a tab with a larger delay.

[1] Something like, rounded to 50ms increments, in a range, 100 - 1000ms. Avoiding collisions / ensuring uniqueness is left as an exercise for the reader.


For anyone else who finds green-on-black hard to read:

    document.body.style.color = "#333333";
    document.body.style.backgroundColor = "#EEEEEE";


Good rant, jwz, and thanks for the micro-knowledge that a) it's impossible to play a sound without a touch event in an iOS browser and b) multi-tab localstorage access can be problematic if you use it to coordinate activity

I think another way to solve this problem is to simply check shared memory to see if the request has been handled by any tab - if so, then don't handle it. You don't need to poll, however you might need to stagger the checks a little since playing a sound can happen so quickly. Note that this implies that every sound triggering event gets a unique id, which can be a timestamp, GUID, hash or something like that. (I'd timestamp and hash every event at the server).


Can anybody explain what this sentence on MDN means?

Normally, scripts on different pages are allowed to access each other if and only if the pages that executed them are at locations with the same protocol (usually both https), port number (443 being the default for https), and host (modulo document.domain being set by both pages to the same value).

https://developer.mozilla.org/en-US/docs/Web/API/Window/post...

What access is it talking about? I get the feeling they are talking about parent-child windows.


It probably means accessing another contexts' global object directly via an iframe (or child window), which works for matching domains and throws a security exception otherwise.


Right... I suppose the author presupposed you have a handle on the other window, which you can only get from window.open(), <iframe>, etc.

Makes me wonder what kind of race conditions can happen between contexts sharing global objects. Are they guaranteed to behave as if they were one context? i.e., single threaded?


I have vague memories of opening new windows with window.open to fill out a form and communicating state back to the parent window. I also did it with iframes, probably to upload a form without a page refresh.


Broadcast Channel API attempts to solve this but only Mozilla has implemented so far,

https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_C...

http://caniuse.com/#feat=broadcastchannel


I used to have this same problem with cookies way back in the day.

If you had a number of windows that all needed to communciate with the streaming server, and share a session, and you closed the master, one of the others would have to take over the connection. We used cookies in a similar way to local storage is used in this story. Ugly ugly code.

These days of course, I'd want to use service workers.


Once you start adding mutexes / atomic compare-and-swap statements you start going down the rabbit hole of concurrent programming (https://twitter.com/themitcho/status/308026012455821312).

Even though there is an apparent shift in the creation of web applications resembling desktop applications more and more, due to security concerns native applications will always have more control over the system and this includes the choices for threading. If not, you're essentially writing a new operating system but now in a browser, following the (too often seen) anti pattern of implementing the same functionality already available but then on a higher level, only resulting in performance loss.

JavaScript for web development should merely be a scripting language, augmenting the functionality of your web page. If, for performance reasons, you require multiple threads then switch to a native app.

Furthermore, if JS is only used to augment the functionality of a page, the costs of the theoretical concurrency issues are negligible and a mere check for whether the sound has been played, if not play the sound and set a flag, if not do nothing should suffice and be correct in the overwhelming majority of cases.


The point of this post is that they've already gone down the rabbit hole of concurrent programming, but none of the standard tools for it are provided.

Performance is irrelevant here. Concurrency isn't being used as a performance tool, it's happening as a consequence of the user's actions. The programmer doesn't want it, but they have to deal with it.


i had to this for webrtc calls before, and I ended up managing it all server side (there was a shared game state on the server, so it was fine).

Although, LocalStorage is the best way i've seen to handle the situation in a single page app, where you log out on one of the open tabs, and the other ones still think they are still logged in.

In general, there's a lot of really "fun" edge cases when it comes to modern web apps and multiple open instances ...


You don't get to just arbitrarily play sounds when I visit your website on my phone, sorry. Yeah, Apple are real dicks for that one.


Correction: LocalStorage is synchronous not asynchronous.


I think it acts asynchronous if you have multiple tabs for the same domain open at once.

The example given was: "it spans multiple documents, and you want a sound to play in reaction to some external, asynchronous event if any of your documents are open."




Consider applying for YC's Spring batch! Applications are open till Feb 11.

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

Search: