
Javascript, locking and sound - AndrewDucker
http://www.jwz.org/blog/2015/07/javascript-locking-and-sound-brought-to-you-by-the-letters-w-t-and-f/
======
timothya
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...](https://developer.mozilla.org/en-
US/docs/Web/API/SharedWorker)

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

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

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

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

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

~~~
jtuchsen
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... :-/

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

~~~
vezzy-fnord
_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.

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

~~~
vezzy-fnord
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.

------
kalmi10
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](https://w3c.github.io/webstorage/#the-storage-event)

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

~~~
nathan-muir
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.

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

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

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

------
tantalor
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...](https://developer.mozilla.org/en-
US/docs/Web/API/Window/postMessage)

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

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

~~~
tantalor
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?

------
tantalor
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...](https://developer.mozilla.org/en-
US/docs/Web/API/Broadcast_Channel_API)

[http://caniuse.com/#feat=broadcastchannel](http://caniuse.com/#feat=broadcastchannel)

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

------
Gladdyu
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](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.

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

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

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

------
scottfr
Correction: LocalStorage is synchronous not asynchronous.

~~~
jaredsohn
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."

