Problem tying a variable to a boolean in P5JS (Simple bouncing ball exercise)

TL;DR Why isn't the ball bouncing off of the edge in the link below?

I'm currently going through the CodingTrain P5JS videos. What I'm trying to achieve is a simple toggle button:

https://editor.p5js.org/S_Oda/sketches/uF_dgcQP-

When the button is 'on' it changes to red and the ball moves until it hits the left or right edge where it will bounce off and change to the opposite direction. When the button is toggled 'off' the ball will freeze in place, it will resume movement when the button is pressed again.

My problem is, when the ball hits the edge, it seems to glitch in place. The bouncing was working before I introduced the toggle button. The resoning for my code is that if I tie the 'speed' variable of the ball to be dependant on a boolean ("ballState"), then the button can just change whether 'ballState' is true or false, i.e. move or not move. So I'm wondering if the problem is here somewhere:

  //Tying speed to ballState so that I can toggle the ball movement with the button
  if (!ballState) {
    speed = 0;
  } else if (ballState) {
    speed = 8;
  }

  //Why does this not result in the ball bouncing off of the edge of the canvas?
  if (x >= width || (x <= -1 && ballState)) {
    speed = speed * -1;
  }

I'd appreciate if someone could tell me what I'm doing wrong, without introducing any new concepts/workarounds, as I'm trying to learn things one step at a time. Thanks very much in advance!

Full code:

var btn = false;
var ballState = false;
var x = 0;
var speed = 0;

function setup() {
  createCanvas(600, 400);
  background(0);
  rectMode(CENTER);
}

function draw() {
  background(0);
  stroke(255);

  //Button
  if (btn) {
    stroke(255, 0, 0);
    fill(255, 0, 0);
    push();
    translate(0, 180);
    text("ON", width / 2, height / 2, 20, 20);
    pop();
  } else {
    noFill(0);
    push();
    translate(0, 180);
    text("OFF", width / 2, height / 2, 20, 20);
    pop();
  }
  rect(width / 2, 330, 120, 60);

  //Ball
  x = x + speed;
  push();
  noStroke();
  fill(0, 0, 255);
  ellipse(x, height / 2, 100, 100);
  pop();

  //Tying speed to ballState so that I can toggle the ball movement with the button
  if (!ballState) {
    speed = 0;
  } else if (ballState) {
    speed = 8;
  }

  //Why does this not result in the ball bouncing off of the edge of the canvas?
  if (x >= width || (x <= -1 && ballState)) {
    speed = speed * -1;
  }
}

//Button toggle
function mousePressed() {
  if (mouseX >= 239 && mouseX <= 360 && mouseY >= 300 && mouseY <= 360) {
    btn = !btn;
    ballState = !ballState;
  }
}

Solution 1:

The problem is mostly this, which runs every frame in the draw function:

// ...
else if (ballState) {
  speed = 8;
}

...where ballState is more aptly named isBallMoving.

You may think speed = speed * -1; reverses the ball, but on the very next rendering frame, ballState being true means that speed = 8; in the above code block puts it right back in the forward direction, overriding your intent.

Another issue to consider is this: if x >= width and you do speed *= -1 to reverse the direction, will the ball's x still be >= width on the next frame? If so, all things being equal, the speed *= -1 will occur again and the ball will just vibrate in place, reversing direction infinitely.

I'll leave it to you to work out how best to solve these problems, but at a high level, you'll want to capture and check something more than ballState for your speed setting logic. You may also need a way for objects that have collided with a wall to "push" themselves back out of the wall until any collision has been resolved.