🌱 idlecycles

Chapter 5: Operating on Values

Up until now, our functions to modify a pattern were very much concerned with the timespan. Let's look at ways to modify the value of a Hap.

withValue

The function `withHap` lets us edit a hap as we like. `withValue` uses it to run a function on the Hap value specifically, returning a new Hap.

With that, we could put together different shades of blue:

Color from Numbers

Operating on values gets more exciting when we have numbers to work with. For that matter, let's update the draw logic that sets the rectangle color:

if (isNaN(Number(value))) {
  ctx.fillStyle = value; // <- like before
} else {
  value = Number(value);
  ctx.fillStyle = `hsl(${value}turn 50% 50%)`;
}

Now, if we receive a valid number, we'll use it as the `hue` value in an `hsl` color! Here's a rainbow pattern:

Arithmetic

Now that we have numbers, we can do some arithmetic!

Using `add` and `mod`, we could rotate the color palette:

..changing the value of `add`:

Object as Values

Right now, we are only able to pattern the hue. Wouldn't it be nice if we could pattern other properties separately, like hue, saturation, lightness, transparency, rect size, ...? We can do this by adjusting our draw code again:

if (typeof value === "object") {
  let {
    color,
    h: hue,
    s: saturation,
    l: lightness,
    a: alpha,
  } = value;
  if (color) {
    ctx.fillStyle = color;
  } else if (hue !== undefined) {
    hue = hue * 360;
    saturation = (saturation ?? 0.5) * 100;
    lightness = (lightness ?? 0.5) * 100;
    alpha = alpha ?? 1;
    ctx.fillStyle = `hsla(${hue},${saturation}%,${lightness}%,${alpha})`;
  }
}

With that in place, our patterns now need to contain objects:

This notation is rather clunky, we need control functions to the rescue!

Control Functions

A `control` is basically a shortcut to get a pattern of objects, which means each `Hap` has an object as value.

Controls are special in the sense that they can be used to create a pattern but also modify a pattern. This allows us to completely avoid curly braces `{}` and keep using functions with method chaining for object values.

Implicit Mini Notation

In Chapter 4, we've implemented mini notation. We can modify the register function to treat any string as mini notation like this:

let register = (name, fn) => {
  let q = (...args) => {
    args = args.map((arg) => {
      if (typeof arg === "string") {
        return mini(arg);
      }
      return arg;
    });
    return fn(...args);
  };
  Pattern.prototype[name] = function (...args) {
    return q(...args, this);
  };
  return q;
};
    

Now we can set our controls with mini notation:

End of Chapter 5

This is starting to look very usable! In the next chapter we will finally make some noise with it

chapter 6: adding sound + joining patterns

all chapters


show page source

    

license: AGPL-3.0

back to garten.salat