P5 Layout Questions

Hi,

I’m working with a p5 video player and I’m not too familiar with CSS. I have a layout that’s consistent on Chrome and Firefox, but for some reason, it gets messed up in Safari and does not line up the way my button and video are set. Additionally, the slider gets placed elsewhere. I tried creating a slider in html and using document.getElementById.value to use the slider for panning, but that doesn’t work so I stuck to creating it in p5.js itself. Can someone help me figure that out? Pasting my code below.

//SKETCH.JS:
let cnv;
let video_file;
let button;
let playButton = document.getElementById("play-button");
let playing = false;
let panValue;// = document.getElementById("sliderRange").value;
let theresa_audio; //left voice
let stan_audio; //right voice
let leftVal, rightVal;
let audioSlider;

function preload() {
  soundFormats('ogg', 'mp3');
  theresa_audio = loadSound('theresa_vocal_solo.mp3'); 
  stan_audio = loadSound('stan_vocal_solo.mp3'); 
}

function centerCanvas() {
  let x = (windowWidth - width) / 2;
  let y = (windowHeight - height) / 2;
  cnv.position(x, y);
}

function setup() {
  cnv = createCanvas(800, 480);
  centerCanvas();
  // specify multiple formats for different browsers
  video_file = createVideo(['assets/theresa_stan_480p_with_instrumental.mp4', 'assets/theresa_stan_480p_with_instrumental.webm']);
  video_file.hide(); // by default video shows up in separate dom
  // element. hide it and draw it to the canvas
  // instead

  playButton.addEventListener('click', togglePlay);

  audioSlider = createSlider(-1, 1, 0, 0.1);
  audioSlider.style('width', '150px');
  audioSlider.position(720, 655, 'relative');
}

function windowResized() {
  centerCanvas();
}

function draw() {
  background(150);
  image(video_file, 0, 0); // draw the video frame to canvas

  panValue = audioSlider.value();
  panAudio();
}

function togglePlay() {
  if(playing) {
    video_file.pause();
    theresa_audio.pause();
    stan_audio.pause();
    playButton.innerText = "Start Video";

    playing = false;
  } else {
    video_file.loop();
    theresa_audio.loop();
    stan_audio.loop();
    playButton.innerText = "Stop Video";

    playing = true;
  }
}

function panAudio() {
  theresa_audio.pan(-1);
  stan_audio.pan(1);

  if (panValue < 0) {
    //sets volume to gradually increase left voice and decrease right voice
    leftVal = panValue * (-1);
    rightVal = 1 - leftVal;

    theresa_audio.setVolume(leftVal);
    stan_audio.setVolume(rightVal);

  } else if (panValue > 0) {
    //sets volume to gradually increase right voice and decrease left voice
    rightVal = panValue;
    leftVal = 1 - rightVal;

    theresa_audio.setVolume(leftVal);
    stan_audio.setVolume(rightVal);

  } else if (panValue == 0) {
    //middle of slider sets max volume for both voices
    //no panning
    theresa_audio.setVolume(1);
    stan_audio.setVolume(1);

  }
}
//INDEX.HTML:
<!DOCTYPE html>
<html>
  <head>
    <title>P5</title>
    <script src="ml5.min.js" type="text/javascript"></script>
    <script src="p5.min.js"></script>
    <script src="p5.dom.min.js"></script>
    <script src="p5.sound.min.js"></script>
   
    <link 
      rel="stylesheet" 
      type="text/css" 
      href="style.css" />
    <link 
      rel="preconnect" 
      href="https://fonts.gstatic.com"  >
    <link 
      href="https://fonts.googleapis.com/css2?family=Nunito+Sans&display=swap" 
      rel="stylesheet"  >
    <link 
      rel="stylesheet" 
      href="style.css" />

    <meta charset="utf-8" />
  </head>

  <body>
    <header id="masthead">
      <h1>P5</h1>
    </header>

    <main id="video">
      <div id="p5_loading">Loading P5.js...</div>
    </main>

    <footer id="bottom-toolbar">
        <div id="audio-controls">
          <button id="play-button">Start Video</button>
        </div>

        <!--
         <div id="slider" class="slidecontainer">
          <input type="range" min="-1" max="1" value="0" class="slider" id="sliderRange" step="0.2">
        </div>
        -->
       
      </div>
    </footer>

    <script src="sketch.js"></script>
  </body>
</html>
//STYLE.CSS
html,
body {
	font-family: 'Nunito Sans', sans-serif;
	color: #444444;
	background-color:#e1f3f8;
	height: 100%;
	width: 100%;
	margin: 0;
	padding: 0;
}

body {
	display: flex;
	flex-direction: column;
	/* align-items: stretch; */
	height: 100%;
	width: 100%;
}

/* Masthead */
#masthead {
	text-align: center;
	background-color:#e1f3f8;
}

/* Web cam */
#video {
	flex: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	background-color: transparent;
}

/* Bottom toolbar */
#bottom-toolbar {
	display: flex;
	align-items: center;
	height: 120px;
	background-color:#e1f3f8;
	padding: 10px;
}

#audio-controls {
	flex: 1;
	text-align: center;
}

button {
	background-color:white;
	color: #444444;
	padding: 8px;
	border: 1px solid #444444;
	border-radius: 8px;
	position: relative;
	left: -75px;
}

If you are going to define elements in HTML and then access them from javascript you need to ensure that the document is finished loading before you attempt to access them. I think it is sufficient to move any uses of document.getElementById to the setup() function (this assumes the setup is only called after document raises the onreadystatechange event and has a readyState value of 'complete').

I was able to make your sketch work by adding the following line to your setup function:

	playButton = document.getElementById("play-button");

Actually I misunderstood the issue you were encountering because when I copied your code to somewhere I could test it I inadvertently moved your <script /> tag into the <head> which introduced an issue that wouldn’t occur with the <script /> tag at the end of the body.

The actual issue with the slider created using createSlider is the use of 'relative' positioning. In CSS, relative positioning causes the element to be offset from where in would normally be placed based on block or inline layout. The large y value for the position of you slider (655) is causing it to display off the bottom of the page (if you scroll down a lot you will see it). The precise layout solution for your slider will depend on exactly where you want it to be positioned.

One thing you might want to try is making your slider created with createSlider a child of the audio-controls div by calling audioSlider.parent('audio-controls');. However, be aware, there seems to be something special about the canvas element that makes it appear above other elements in the stacking order. So you might want to set the z-index of audio-controls to 1 (alternately setting element positions to 'relative' does seem to magically pop them above canvas in the stacking order). CSS is bonkers sometimes :crazy_face:.

Got it. Tried adding those two lines of code but didn’t seem to make a difference. What layout solution would I have to set rather than relative in order to keep the slider lined up to the right of the start button?

Here’s an example (If you click play I apologize for the terrible choice of audio files, they’re just what I had lying around).

Basically all I did was add

	audioSlider.parent('audio-controls');

And remove

	// audioSlider.position(720, 655, 'relative');

I also commented the relative positioning of button:

button {
	background-color: white;
	color: #444444;
	padding: 8px;
	border: 1px solid #444444;
	border-radius: 8px;
	/*
	position: relative;
	left: -75px; */
}

Because this is generally not a great way to adjust positioning. If you want to create space between the button and slider you should use margin.

And lastly I set the z-index for bottom-toolbar and eliminated the background so that it would show above the canvas:

#bottom-toolbar {
	display: flex;
	align-items: center;
	height: 120px;
	padding: 10px;
	z-index: 1;
}