javascripthtmlgithubgithub-pagesliveserver

Why does audio play locally but not on liveserver or Github Pages?


For whatever reason no sound plays in my project https://github.com/Jocowl25/audioVisualizer.
There is no error message in the console, and the sound seems to still be loaded (ex. the song length is visible), yet it isn't playable.
(In Safari the playhead moves when unpaused, while in Chrome it stays in place. It can be dragged in both browsers.)
Everything works perfectly when downloaded and run locally.
It has the ability to play preloaded files and uploaded files, and neither works.

The audio is becomes playable when const source = audioCtx.createMediaElementSource(audio); is commented out.
audio is connected to an html audio tag.
Adding source.connect(audioCtx.destination); does not reconnect the audio.

This is not a CORS error; when the mp3 files show up in inspect element (which occurs seemingly randomly), they are marked as being same-origin.

The audio is still inaudible when user input is provided.

UPDATE: The program appears to work using live server in Github Codespaces, but only with Chrome.

UPDATE 2: It turns out that, on Safari only, local live server does work when the browser is opened for the first time. Once it has been opened in the browser, it won't work until Safari is quit and reopened again.

As a general note I am only able to do tests on Safari and Chrome.

It won't have the sample audio loaded from here, but here is the full code:

window.addEventListener("load", function () { //added by SO recommendation, changed nothing
//colors
const colorPallette=[
["#060647","#0209d1",(i)=>`rgb(105 25 ${(i/FreqBufferLength) * 115+(255-115)})`],
["#fadb61","#b00202",(i)=>`rgb(${(i/FreqBufferLength) * 15+(255-15)} 100 25)`],
]
let colorOption=0
//set up list of default songs
const audioMap = new Map();
audioMap.set("Sample1.mp3","./Sample1.mp3")
audioMap.set("Sample2.mp3","./Sample2.mp3")
//set up select and file input
const select=document.querySelector("select")
const fileInput = document.querySelector('input[type="file"]');
//set up audio
const audio=document.querySelector("audio")
const audioCtx = new AudioContext();
const source = audioCtx.createMediaElementSource(audio);
//set up analyser
const analyser = audioCtx.createAnalyser();
source.connect(analyser);
source.connect(audioCtx.destination);
//set up analyser arrays
const FreqBufferLength = analyser.frequencyBinCount;
const TimeBufferLength = analyser.fftSize;
const timeArray = new Uint8Array(TimeBufferLength);
const freqArray = new Uint8Array(FreqBufferLength);
//set up canvas
const canvas=document.querySelector("canvas")
canvas.width=600;
canvas.height=300;
const w=canvas.width;
const h=canvas.height;
const canvasCtx=canvas.getContext("2d");
canvasCtx.lineWidth = 2;
//set up document events
document.querySelector(".color").addEventListener("click",()=>
  {colorOption++
    colorOption=colorOption%colorPallette.length
  })
document.querySelector(".fileButton").addEventListener("click",()=>fileInput.click())
  fileInput.addEventListener('change', (event) => {
    for (const file of fileInput.files){
      const url = URL.createObjectURL(file);
      canvasCtx.fillStyle = colorPallette[colorOption][0];
      canvasCtx.fillRect(0, 0, w, h);
      select.innerHTML=`<option>${file.name}</option>`+select.innerHTML
      audio.src=url
      audioMap.set(file.name, url);
      select.value=file.name
    }
  })
//start
let val=select.value
requestAnimationFrame(draw)

function draw(){
  //set song
  if(val!=select.value){
  audio.src=audioMap.get(select.value)
  val=select.value
  }
  //pause visualizer if paused
    if(!audio.paused){
      analyser.getByteFrequencyData(freqArray);
      analyser.getByteTimeDomainData(timeArray);
    }
    //set up canvas colors
    canvasCtx.fillStyle = colorPallette[colorOption][0];
    canvasCtx.fillRect(0, 0, w, h);
let x=0
let skip=5
///////////////
///frequency///
///////////////
const barWidth = (w / FreqBufferLength)*1.5*skip;
let barHeight;
x = 0;
for (let i = 0; i < FreqBufferLength; i+=skip) {
    barHeight = freqArray[i];
    canvasCtx.fillStyle = colorPallette[colorOption][2](i);
    canvasCtx.fillRect(x, (h/2)-barHeight/2, barWidth, barHeight+1);
  
    x += (barWidth);
  }
  canvasCtx.strokeStyle = colorPallette[colorOption][1];
  canvasCtx.beginPath();
  x = 0;  
  ////////
  //time//
  ////////
  let timeSkip=1
  const sw = (w / TimeBufferLength)*timeSkip;
    for(let i=0;i<TimeBufferLength;i+=timeSkip){
    const v = timeArray[i] / 128.0;
  const y = v * (h / 2);

  if (i === 0) {
    canvasCtx.moveTo(x, y);
  } else {
    canvasCtx.lineTo(x, y);
  }
  x += sw;
    }
    canvasCtx.stroke();
    canvasCtx.closePath();
    requestAnimationFrame(draw)
}
})
body{
background-color: black;
overflow-x: hidden;
}
h1{
    color:white;
    font-size:70px;
    margin-bottom:30px;
    font-family:sans-serif;
}
canvas{
width:600px;
height:300px;
border: 2px solid rgb(255, 255, 255);
border-radius:20px;
position:relative;
z-index:0;
}
audio{
    height:50px;
    z-index:100;
    position:relative;
}
select{
    margin-bottom:5px;
    height:30px;
    font-size: 15px;
    width:300px;
    text-align:center;
    border-radius:5px;
}
.fileButton{
    margin-bottom:5px;
    font-size: 20px;
    width:300px;
    height:50px;
    align-items:center;
    border-radius:20px;
    border:none;
}
.color{
    margin-top:5px;
    font-size: 20px;
    height:50px;
    width:300px;
    align-items:center;
    border-radius:50px;
    border:none;
}

input{
    display:none;
}
.flex{
    display:flex;
    width:100vw;
    height:100%;
    align-items:center;
    justify-content: center;
    flex-flow:column;
}
button:hover{
    background-color:rgb(191, 187, 187);
}
button{
    background-color:rgb(255, 255, 255);
}
button:active{
    background-color:rgb(168, 161, 161);
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="shortcut icon" href="#">
    <title>Audio Visualizer</title>
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <div class="flex">
      <h1>Audio Viewer</h1>
    <input multiple type="file" id="input"/>
    <button class="fileButton">Add Song</button>
    <select>
      <option>Sample1.mp3</option>
      <option>Sample2.mp3</option>
    </select>
    <canvas></canvas>
    <audio preload="auto" controls src="./Sample1.mp3"></audio>
    <!-- Having cors:anonymous changes nothing -->
    <button class="color">Change Theme</button>
  </div>
    <script src="index.js"></script>
  </body>
</html>


Solution

  • It turns out that AudioContext() counts as autoplaying, even though no audio is being played, and it is blocked from running. Solution is to not create AudioContext() until an input is provided.

    Answer from jcnews1 on Reddit: https://www.reddit.com/r/CodingHelp/comments/1ji1tvn/comment/mjn0kra/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button