Skip to content

Rate

A common pattern in interactivity and motion is changing values over time. For example, maybe we want an object to appear to float, slowly sinking over time.

A common approach to achieve this is to change a value by a fixed amount every time an update loop happens:

let state = {
value: 0
}
function update() {
state = {
...state,
value: value + 0.01
}
}

Although this is mostly fine, there are three challenges:

  1. The amount of change (in the above case, 0.01) is tied to the speed of update() being called. For example, if you end up calling update() less often, you’ll need to increase the number to get a similar rate of change.
  2. Even if you try to call update() at a fixed rate (eg. using setInterval()) timing inconsistencies mean that the change amount can be applied unevenly
  3. Values might seem less readable than talking about change per second, per minute etc.

ixfx has two functions which compute a slice of an amount over time.

In its basic usage, call perSecond with the amount you want to correspond to one second of elapsed time. You get back a function which yields a number.

import { Sources} from 'ixfx/modulation.js';
// Create the function once
const gain = Sources.perSecond(100);
// ...and then call the returned function whenever
// we want the amount that has elapsed
const value = gain();

Every time you call the returned function, you get a proportion of the original amount depending on how much time has elapsed. For example, if we had 100 as the amount:

  • Calling twice within 1 second will each yield 50 (totalling 100 over 1sec)
  • Calling once after 2 seconds will yield 200

When you call the function the accumulated value resets.

If we give the clamp option, it will not let the return value exceed the original amount

// Even if we call increaser() after several seconds, it will
// at most yield 100.
const increaser = Sources.perSecond(100, { clamp: true });

Example

In the demo sketch layout, you might want to use perSecond as part of state computation.

Here’s an example of that, as used in the demo below.

import { Sources } from 'ixfx/modulation.js';
import { resolveFields } from 'ixfx/data.js';
const settings = {
// How much to increase inflation when there's a click
pumpAmount: 0.04,
// How quickly to deflate
deflationRate: 0.02
};
let state = {
// Amount of inflation on 0..1 scale
inflation: 1,
// Function that returns amount to deflate per second
deflator: Sources.perSecond(settings.deflationRate)
};
async function update() {
// 'resolve fields', which will automatically run 'deflator' function for us
// and return back an object with plain values:
let { inflation, deflator } = await resolveFields(state);
// Compute new inflation value
inflation = clamp(inflation - deflator);
// Save new state
saveState({ inflation });
window.requestAnimationFrame(update);
}
// ...other parts of code omitted...

Demo