Skip to content

Randomness

Daeinc edited this page Feb 16, 2024 · 2 revisions

It is common to use some level of randomness in creative coding sketches. Using Math.random() is simple and easy but it is impossible to reproduce the same output twice as it is not seeded.

Ssam provides the Git image snapshot feature via vite-plugin-ssam-git to easily archive and retrieve earlier sketch iterations. By using a seeded PRNG, we can also reproduce the exact same random states. In other words, your code and the image will be 100% reproduced.

In the sketch below, we are using @thi.ng/random package for the PRNG functions. You can try different random seeds, make changes to the code and create a few git snapshots that you like. The saved image filename includes the random seed as well as the commit hash. If you want to go back to any of the sketch versions that the images came from, you can checkout a Git commit with the commit hash and then update the seed. It will reproduce the exact same code and the image you saved.

Another bonus of using the seeded PRNG is that when the sketch is hot reloaded, it will maintain all the random states of your sketch seamlessly.

import { map } from "@daeinc/math";
import { Smush32 } from "@thi.ng/random";
import { ssam } from "ssam";
import type { Sketch, SketchSettings } from "ssam";

const seed = 44401566;
const rnd = new Smush32(seed);

const sketch: Sketch<"2d"> = ({ wrap, context: ctx, width, height }) => {
  if (import.meta.hot) {
    import.meta.hot.dispose(() => wrap.dispose());
    import.meta.hot.accept(() => wrap.hotReload());
  }

  const numCircles = rnd.minmaxInt(3, 12);
  const circles = Array.from({ length: numCircles }).map((c, i) => ({
    rad: map(i, 0, numCircles, 300, 50),
    h: rnd.minmaxInt(0, 360),
    s: rnd.minmaxInt(0, 100),
    l: rnd.minmaxInt(30, 80),
  }));

  wrap.render = ({ playhead }) => {
    ctx.fillStyle = `pink`;
    ctx.fillRect(0, 0, width, height);

    circles.forEach((c) => {
      ctx.beginPath();
      ctx.arc(width / 2, height / 2, c.rad, 0, Math.PI * 2);

      ctx.fillStyle = `hsl(${
        (c.h + Math.sin(playhead * Math.PI * 2) * 90) % 360
      }, ${c.s}%, ${c.l}%)`;
      ctx.fill();
      ctx.strokeStyle = `black`;
      ctx.stroke();
    });
  };
};

const settings: SketchSettings = {
  dimensions: [600, 600],
  pixelRatio: window.devicePixelRatio,
  animate: true,
  duration: 8_000,
  suffix: `-${seed}`, // include seed in filename
};

ssam(sketch, settings);
Clone this wiki locally