Multiple frame rates at any speed from 1 to 30FPS

Hi, I am working in P5.JS and I am trying to achieve multiple frame rates for multiple animations that can have any framerate between 1FPS to 30FPS.

Here is the main question: How to keep the master FPS at frameRate(30) and then have any number of animations be at any FPS between 1-30.

The mod function will allow me to get a few divisions of FPS but this is not good enough as it goes from 30FPS and then 15FPS and that’s a huge difference in speed:

if (frameCount % 2)

// Animate every other frame ~15FPS

if (frameCount % 3)

// Animate one frame out of three

https://p5js.org/reference/#/p5/millis

So there is the timer function in milliseconds which seems like a perfect solution to achieve any FPS I want, but it simply does not keep up with 30FPS, where the same code with no timer and the standard 30 FPS runs perfectly. So it seems this method is not usable for animations?

And then there is deltaTime but I have not been able to figure it. From its description it seems to be exactly what I need but the example on the website is about moving an object on the screen at different rates vs I need the ability to change the state of an object at any FPS , like the background color, as shown in my example code.

The code provided is a simplified version of the main code but it represents the problem well. I’m interested in how else this can be done or if I’m missing something!

Thanks,
4D

let snowflakes = []; // array to hold snowflake objects
let backgroundColor;
let colorflip = true;
let frameRateManager;
let intervalMillis;
let desiredFrameRate = 2;
let FRAMERATEGLOBAL = 30;

function setup() {
  createCanvas(400, 600);
  fill(240);
  noStroke();
  frameRate(FRAMERATEGLOBAL)
  frameRateManager = new FrameRateManager()
  backgroundColor = color('red');

  intervalMillis = 33
}

function draw() {
  
  setTimeout(function runFlow() {
    if(colorflip) {
      backgroundColor = color('pink');
      colorflip = false;
    } else {
      backgroundColor = color('cyan');
      colorflip = true;
    }
  }, intervalMillis);

  let t = frameCount / 60; // update time

  // create a random number of snowflakes each frame
  for (let i = 0; i < random(5); i++) {
    snowflakes.push(new snowflake()); // append snowflake object
  }

  // loop through snowflakes with a for..of loop
  // FrameRateManager using mod to control when to make changes at a particular frame rate (or as close as possible)
  // if(frameRateManager.drawDesiredFPS(desiredFrameRate)) {
  //   if(colorflip) {
  //     backgroundColor = color('pink');
  //     colorflip = false;
  //   } else {
  //     backgroundColor = color('cyan');
  //     colorflip = true;
  //   }
  //}



  background(backgroundColor);
  for (let flake of snowflakes) {
    flake.update(t); // update snowflake position
    flake.display(); // draw snowflake
  }
}

// snowflake class
function snowflake() {
  // initialize coordinates
  this.posX = 0;
  this.posY = random(-50, 0);
  this.initialangle = random(0, 2 * PI);
  this.size = random(2, 5);

  // radius of snowflake spiral
  // chosen so the snowflakes are uniformly spread out in area
  this.radius = sqrt(random(pow(width / 2, 2)));

  this.update = function(time) {
    // x position follows a circle
    let w = 0.6; // angular speed
    let angle = w * time + this.initialangle;
    this.posX = width / 2 + this.radius * sin(angle);

    // different size snowflakes fall at slightly different y speeds
    this.posY += pow(this.size, 0.5);

    // delete snowflake if past end of screen
    if (this.posY > height) {
      let index = snowflakes.indexOf(this);
      snowflakes.splice(index, 1);
    }
  };

  this.display = function() {
    ellipse(this.posX, this.posY, this.size);
  };
}

class FrameRateManager {
  constructor() {
    this.currentFrame = 0
  }

  drawDesiredFPS(desiredFrameRate) {
    this.incrementCurrentFrame();
    var fpsMod = FRAMERATEGLOBAL / desiredFrameRate;
    return ((this.currentFrame % (Math.round(fpsMod))) === 0 && (this.currentFrame != 0)) ? true : false;
  
  }

  incrementCurrentFrame() {
    (this.currentFrame >= FRAMERATEGLOBAL) ? (this.currentFrame = 0) : (this.currentFrame += 1);
  }
}```