mattdesl / canvas-sketch

[beta] A framework for making generative artwork in JavaScript and the browser.
MIT License
4.97k stars 393 forks source link

Randomness Not Reproducible #155

Closed nysky1 closed 2 years ago

nysky1 commented 2 years ago

Matt and community, I've tried for a few days now to ensure that I can regen an image based on a seed. Tell me why this scenario does NOT work:

I'm running this example from the /examples folder. I CMD-R to refresh the images. I see an image I like - I press CMD+S. The browser saves the image with the seed as a suffix. I go back, comment out line 5 and uncomment line 6, then adding the seed to line 6 below (see <----- comment) I rerun with the hard-coded seed and get a different image than expected.

Have a misread the docs or am I doing something wrong? Thank you!


const canvasSketch = require('canvas-sketch'); const Random = require('canvas-sketch-util/random'); const { lerp } = require('canvas-sketch-util/math');

// We can force a random seed or a specific string/number Random.setSeed(Random.getRandomSeed()); //Random.setSeed(340593); <--------

const settings = { pixelsPerInch: 300, // When exporting, use the seed as the suffix // This way we can reproduce it more easily later suffix: Random.getSeed(), // Standard A4 paper size dimensions: 'A4', // We'll work in inches for the rendering units: 'in' };

const sketch = ({ width, height }) => { const margin = 0;

const sliceCount = 50000; const slices = Array.from(new Array(sliceCount)).map((_, i, list) => { const t = list.lenth <= 1 ? 0 : i / (list.length - 1);

const noiseAngle = t * Math.PI * 2;
const nx = Math.cos(noiseAngle);
const ny = Math.sin(noiseAngle);
const nf = 0.05 + Random.range(0, 0.5);
const amplitude = 2;
const noise = Random.noise2D(nx * nf, ny * nf);
const noise01 = noise * 0.5 + 0.5;

const tOffset = Random.gaussian(0, 0.01);

const cx = width / 2;
const x = cx + noise * amplitude;
return {
  alpha: Random.range(0.75, 1) * (1 - noise01),
  color: 'white',
  lineWidth: Random.range(0.005, 0.02) * 0.1,
  length: Random.gaussian() * noise01 * 0.5,
  angle: Random.gaussian(0, 1),
  x,
  y: lerp(margin, height - margin, t + tOffset)
};

});

return ({ context }) => { context.globalCompositeOperation = 'source-over'; context.fillStyle = 'black'; context.fillRect(0, 0, width, height); context.globalCompositeOperation = 'lighter';

slices.forEach(slice => {
  context.save();
  context.beginPath();
  context.translate(slice.x, slice.y);
  context.rotate(slice.angle);
  context.lineTo(slice.length / 2, 0);
  context.lineTo(-slice.length / 2, 0);
  context.lineWidth = slice.lineWidth;
  context.strokeStyle = slice.color;
  context.globalAlpha = slice.alpha;
  context.stroke();
  context.restore();
});

}; };

canvasSketch(sketch, settings);

L-A commented 2 years ago

FWIW, this is about canvas-sketch-util, not really canvas-sketch, but it's probably useful to others:

You're getting this behaviour because when you save an image, the PRNG has already stepped forward every time you called random.something, and it will be in a different state the start of the next drawing call.

To get a consistent state and output in this situation, you have to store the value of getRandomSeed() at the top, then re-apply it to setSeed at the beginning of your sketch function:

// Seeding at the top

const Seed = random.getRandomSeed();
random.setSeed(Seed);
// Then setting a constant state when starting to draw

  const sketch = () => {
  return ({ context, width, height }) => {
    random.setSeed(Seed); // <- Re-apply your seed before you call your first random
    [...]

You could also generate a separate getRandomSeed() value specifically for your drawing routine, so long as you consistently apply it at the beginning of your sketch function.

nysky1 commented 2 years ago

Thank you LL! It also turns out that the Seed is a string not an integer. Thanks for helping today!

J.

On Mon, May 16, 2022 at 1:21 PM Louis-André Labadie < @.***> wrote:

FWIW, this is about canvas-sketch-util, not really canvas-sketch, but it's probably useful to others:

You're getting this behaviour because when you save an image, the PRNG has already stepped forward every time you called random.something, and it will be in a different state the start of the next drawing call.

To get a consistent state and output in this situation, you have to store the value of getRandomSeed() at the top, then re-apply it to setSeed at the beginning of your sketch function:

// Seeding at the top const Seed = random.getRandomSeed();random.setSeed(Seed);

// Then setting a constant state when starting to draw

const sketch = () => { return ({ context, width, height }) => { random.setSeed(Seed); // <- Re-apply your seed before you call your first random [...]

You could also generate a separate getRandomSeed() value specifically for your drawing routine, so long as you consistently apply it at the beginning of your sketch function.

— Reply to this email directly, view it on GitHub https://github.com/mattdesl/canvas-sketch/issues/155#issuecomment-1128046756, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABR3XBEL7OAMOXN6TQNNKZTVKKN37ANCNFSM5WCM2N5A . You are receiving this because you authored the thread.Message ID: @.***>

nysky1 commented 2 years ago

See Matt's post in this issue related to pure functions.

https://github.com/mattdesl/canvas-sketch/issues/56

On Wed, May 18, 2022 at 4:58 AM Asbjorn Enge @.***> wrote:

Hi @L-A https://github.com/L-A 😊

I'm using the seed-random module and I still have this problem 🤔 I can reload as many times as I like and the output is always the same. But pressing ctrl+s changes the output.

const random = require('canvas-sketch-util/random')

const seedRandom = require('seed-random')

const seed = 'myhardcodedseedfortesting'

seedRandom(seed, { global: true })

const seeded = random.createRandom(seed)

console.log('seed', seed)

Any ideas?

— Reply to this email directly, view it on GitHub https://github.com/mattdesl/canvas-sketch/issues/155#issuecomment-1129865804, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABR3XBBZVZJ6AAH4EXKW74DVKTEMDANCNFSM5WCM2N5A . You are receiving this because you modified the open/close state.Message ID: @.***>