What methods can be utilized to create sound effects in presentations using CSS?

Let us begin by acknowledging that:

  • HTML is primarily for structure
  • CSS mainly deals with presentation
  • JS focuses on behavior

Note: The discussion of whether presentation that responds to user interaction is essentially another term for behavior is open to interpretation. One perspective holds this view.

However, the myriad interactive visual effects seen across the web created solely in CSS and triggered by :hover, :focus, :focus-within, and other pseudo-class states suggests a different widely accepted viewpoint – that CSS is a valid technology for managing what could be termed as interactive presentation.

I'm curious (as I couldn't find any information) if, despite the numerous ways CSS is used for handling interactive visual presentation, it has ever been utilized for dealing with interactive aural presentation?

  1. Have there been endeavors to incorporate aural presentation into CSS?

  2. Is there a proposal to include aural presentation in CSS?

Is there any official strategy at all (even if not yet implemented by browsers) to achieve what the following demo accomplishes using these hypothetical CSS properties:

  • audio
  • audio-duration
  • audio-play

Working Example:

For now, let’s ignore the hastily written JavaScript below and imagine that CSS styles like these:

.square:unhover {
  audio: url(//cdn.freesound.org/previews/628/628357_1486333-lq.mp3);
  audio-duration: 1.776s;
  audio-play: 2;
}

simply work as they are supposed to.

// BUILD STYLESHEET ARRAY
const stylesheet = document.head.querySelector('style');
let stylesheetText = stylesheet.textContent.replace(/\s/g, '');
let stylesheetArray = stylesheetText.split('}');
stylesheetArray.pop();

// BUILD SELECTORS ARRAY
let selectorsArray = [];
for (let i = 0; i < stylesheetArray.length; i++) {
  selectorsArray[i] = {
    selector: stylesheetArray[i].split('{')[0],
    styles: {}
  };
  
  let selectorRules = stylesheetArray[i].split('{')[1].split(';')
  selectorRules.pop();
  
  for (let j = 0; j < selectorRules.length; j++) {
  
    selectorsArray[i].styles[selectorRules[j].split(':')[0]] = selectorRules[j].split(':')[1];
  }
}

// BUILD TARGET QUERIES ARRAY
let targetQueriesArray = [];

for (let i = 0; i < selectorsArray.length; i++) {
  
  if ((/:hover|:unhover/).test(selectorsArray[i].selector)) {
  
    if (Object.keys(selectorsArray[i].styles).includes('audio')) {
  
      let targetQuery = selectorsArray[i];
      
      switch (true) {
    
        case (targetQuery.selector.includes(':hover')): targetQuery.event = 'mouseover'; break;
        case (targetQuery.selector.includes(':unhover')): targetQuery.event = 'mouseout'; break;
      }
    
      targetQueriesArray.push(targetQuery);
    }
  }
}

// DECLARE playAudioViaCSS FUNCTION
const playAudioViaCSS = (audioURL, audioDuration, audioPlays) => {

  let soundEffectViaCSS = new Audio(audioURL);
  soundEffectViaCSS.loop = true;
  soundEffectViaCSS.play();
  
  setTimeout(() => soundEffectViaCSS.loop = false, (audioDuration * audioPlays));
}

// ATTACH AUDIO EVENTS TO TARGET ELEMENTS
for (let i = 0; i < targetQueriesArray.length; i++) {
 
  let targetAudio = targetQueriesArray[i].styles.audio.replace('url(', '').replace(')', '');
  let targetAudioDuration = (targetQueriesArray[i].styles['audio-duration'].replace('s', '') * 1000) || 1000;
  let targetAudioPlays = targetQueriesArray[i].styles['audio-play'] || 1;
  let targetEvent = targetQueriesArray[i].event;
  let targetQuery = targetQueriesArray[i].selector.replace(/:hover|:unhover/g, '');

  let targetElements = [...document.querySelectorAll(targetQuery)];
  targetElements.forEach((element) => element.addEventListener(targetEvent, () => playAudioViaCSS(targetAudio, targetAudioDuration, targetAudioPlays), false));
};
:root {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
}

.square {
  width: 100px;
  height: 100px;
  line-height: 100px;
  text-align: center;
  color: rgb(255, 255, 255);
  background-color: rgb(255, 0, 0);
  border-radius: 50%;
  cursor: pointer;
  transform: scale(1);
  transition: all 0.9s ease-out; 
}

.square:hover {
  border-radius: 0;
  transform: scale(1.5);
  audio: url(//cdn.freesound.org/previews/354/354062_1490240-lq.mp3);
  audio-duration: 2.304s;
  audio-play: 3;
}

.square:unhover {
  audio: url(//cdn.freesound.org/previews/628/628357_1486333-lq.mp3);
  audio-duration: 1.776s;
  audio-play: 2;
}
<div class="square">
Hover me
</div>

Answer №1

When it comes to web design, CSS is the key player in determining the visual appearance of a website. With CSS, designers have the power to customize text colors, font styles, paragraph spacing, column layouts, and more. One of the great benefits of using CSS is its time-saving capability - After writing CSS code once, you can easily apply the same styling across multiple HTML pages.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Dynamic Select Option housing Bootstrap Popover

I am attempting to create a popover that generates dynamic content and reacts when an option is selected. On my page, I have a select element and one of the options should trigger a popover containing a form with 'Accept' and 'Cancel' ...

What causes the error message "ng-model is undefined"?

Within my form, there are three angular-ui-bootstrap typehead elements <form> <div class="form-group"> <label for="fieldname" class="col-md-3 control-label">Name</label> <div class="col-md-6"> <input type="text ...

The peculiar behavior of Google Maps markers refreshing inconsistently upon bounds adjustment

I recently completed a project involving a restaurant app using Vue and Google Maps. While everything is functional, I have encountered a persistent bug with the markers. When navigating on the map and the bounds change, some markers seem to disappear. Ev ...

How can I incorporate fetch into my code using VSCode?

I am completely new to using JS. I want to incorporate fetch in VSCode, but I'm having trouble importing it. When I try: import fetch from "node-fetch"; I get this error: (node:19856) Warning: To load an ES module, set "type": &q ...

Looking for a demonstration using dust.js or handlebars.js in a two-page format with express3.x and node?

Currently, I am in the process of selecting a templating engine to use. While I have come across numerous single-page examples utilizing template engines, I am specifically searching for a practical example that demonstrates handling two distinct pages whi ...

JS Issue with Generating Content

Introduction( kind of :) ) Starting with the process of generating 5 coffee beans for each cup of coffee. The coffee class includes a strength attribute, and my goal is to visually distinguish the coffee beans which have a strength greater than the select ...

The combination of DOMDocument and DOMXPath is a powerful duo

I am currently working on converting CSS to inline using DOMDocument and DOMXPath. However, I'm encountering an issue where the HTML I provide is not being recognized as valid XML. Is there a way for me to configure these functions to ignore unknown ...

Exploring ways to check async calls within a React functional component

I have a functional component that utilizes the SpecialistsListService to call an API via Axios. I am struggling to test the async function getSpecialistsList and useEffect functions within this component. When using a class component, I would simply cal ...

Performing date comparison in JavaScript with varying date formats

My system includes a table that retrieves dates from an FTP location. On the user interface page, there is a form that gathers all details related to a specific FTP date. However, I am facing difficulties in comparing the FTP dates with those specified in ...

Is it possible to expand the Angular Material Data Table Header Row to align with the width of the row content?

Issue with Angular Material Data Table Layout Link to relevant feature request on GitHub On this StackBlitz demo, the issue of rows bleeding through the header when scrolling to the right and the row lines not expanding past viewport width is evident. Ho ...

What is the best way to structure a JSON object to support conditional statements?

My goal is to develop a JSON object capable of handling conditionals/branching. My workflow involves multiple steps where the user's choices determine the subsequent set of options. This branching logic continues throughout different stages. I envisi ...

Refreshing the View in Ionic and AngularJS Using Controller UpdatesIn this tutorial, we will

As a newcomer to both Ionic and Angularjs, I am currently in the process of developing a simple Ionic app. The main functionality involves displaying a list of classes (sessions), allowing users to book or cancel a class by clicking on an icon, and updatin ...

What is the solution for - Uncaught TypeError: Cannot access the property 'scrollHeight' of an undefined element?

Could someone kindly assist me in resolving an issue? I recently encountered a problem with a script I have been using on the Chrome console. This script has been working perfectly fine for the past few days. However, today when I ran it, I started receiv ...

Node.js and Express make it easy to provide XLS download functionality

I have successfully utilized the code snippet below to generate an excel file in node.js. My intention is for this generated file to be downloadable automatically when a user clicks on a designated download button. var fs = require('fs'); var w ...

Different ways to enhance max-http-header-size in Vue application

After being redirected from another application, I am unable to open the page and receive an error in the console: Failed to load resource: the server responded with a status of 431 (Request Header Fields Too Large). I came across information about max-h ...

A guide to efficiently filling multiple rows of images with different heights while preserving their aspect ratio

I am facing a design challenge that I believe can be solved using CSS, although it might require some Javascript. My issue involves a gallery of images with varying sizes and proportions. I would like to achieve the following: Distribute the images in m ...

Custom JSON filtering

I have a JSON object called Menu which contains various menu items for my system that I am developing using VueJS + Vuetify. I need to filter these menu items based on the "text" field, regardless of position in the text and without differentiating between ...

Tips for converting a select option into a button

Currently, I am working with Laravel to develop my shopping cart. My goal is to implement a feature that allows customers to select the quantity of a product and have the price update accordingly when they click on a specific number. However, I am facing a ...

Importing classes in ECMAScript 6 does not work properly, causing issues when running scripts

I am currently learning Selenium Webdriver. I am facing an issue where I can't run a script with imported classes, but I am able to run it without classes using import functions only. To execute it, I use babel-cli in the following manner: node ./babe ...

How can JQuery determine the current CSS background image of a three-state icon or button?

I am facing a challenge with using three images to represent different states of an application icon, and I am trying to achieve this using CSS. There are no issues with the original or hover states: #myDIV { background-image: url(icons/homeBlack.png); } ...