CSS is the domain of styling, layout, and presentation. It is full of colors, sizes, and animations. But did you know that it could also control when a sound plays on a web page?
This article is about a little trick to pull that off. It’s actually a strict implementation of the HTML and CSS, so it’s not really a hack. But… let’s be honest, it’s still kind of a hack. I wouldn’t recommend necessarily using it in production, where audio should probably be controlled with native
There are a few alternatives to playing sounds with CSS, but the underlying idea is the same: inserting the audio file as a hidden object/document within the web page, and displaying it whenever an action happens. Something like this:
This code uses an
tag, but we could also use
with similar results:
A quick note on the demo and this technique. I developed a small piano on CodePen just with HTML and CSS using this technique about a year ago. It worked great, but since then, some things have changed and the demo doesn’t work on CodePen anymore.
The biggest change was related to security. As it uses
object instead of
audio, the imported file is subject to stricter security checks. Cross-origin access control policies (CORS) force the audio file to be on the same protocol and domain as the page it is imported into. Even putting the sound in base64 will not work anymore. Also, you (and users) may need to activate autoplay on their browser settings for this trick to work. It is often enabled behind a flag.
Another change is that browsers now only play the sounds once. I could have sworn that past browsers played the sound every time that it was shown. But that doesn’t appear to be the case anymore, which considerably limits the scope of the trick (and bares the piano demo almost useless).
The CORS issue can be worked around if you have control over the servers and files, but the disabled autoplay is a per-user thing that is out of our control.
Why it works
The theory behind this behavior can be found buried in the definition of the
embedelement that was not potentially active becomes potentially active, and whenever a potentially active
embedelement that is remaining potentially active and has its
srcattribute set, changed, or removed or its
typeattribute set, changed, or removed, the user agent must queue a task using the embed task source to run the
embedelement setup steps for that element.
Same goes for the definition of the
Whenever one of the following conditions occur:
- the element changes from being rendered to not being rendered, or vice versa,
While it is clearer for
object (the file is processed and run on render), we have this concept of “potentially active” for
embed that may seem a bit more complicated. And while there are some additional conditions, it will run on initial render similarly as how it does with
As you can see, technically this is not a trick at all, but how all browsers should behave… although they don’t.
As with many of these hacks and tricks, the support of this feature is not great and varies considerably from browser to browser.
It works like a charm on Opera and Chrome, which means a big percentage of the browser market. However, the support is spotty for other Chromium-based browsers. For example, Edge on Mac will play the audio correctly, while the Brave browser won’t unless you have the latest version.
Safari was a non-starter, and the same can be said for Internet Explorer or Edge on Windows. Nothing close to working on any of these browsers.
Firefox will play all the sounds at once on page load, but then won’t play them after they are hidden and shown again. It will trigger a security warning in the console as the sounds attempt to be play “without user interaction,” blocking them unless the user approves the site first.
Overall, this is a fun trick with CSS but one of those “don’t do this at home” kind of things… which in software development, means this is the perfect thing to do at home. ?