Random
random is seedable and lives on the sketch, so two sketches never share hidden global state. The seed is entropy-based by default, so an unseeded sketch differs each run. Set a seed when you want a reproducible image. The generator is Ollin's own (SplitMix64), so a seed reproduces Ollin's output rather than p5's. For smooth, coherent values instead, see Noise.
Contents
- random
- randomGaussian
- randomVector
- randomVector, in a ring
- randomChoice
- shuffled
- MarkovChain, a choice that remembers
- randomSeed
- seed
- randomness
Generators
random
random() -> Double
random(_ max: Double) -> Double
random(_ min: Double, _ max: Double) -> Double
A uniform random Double, in 0..<1, in 0..<max, or between min and max. The two-bound form is order-independent, so it takes the bounds either way round.
let y = 400 + random(-100, 100) // jitter around 400
if random() < 0.2 { /* runs about a fifth of the time */ }
A zero-width range short-circuits, so random(a, a) returns a without consuming a roll. That matters in a seeded sketch. A parameter or an animated value can scale a jitter amount down to exactly zero. In that case, write the jitter as a scaled unit roll, random(-1, 1) * amount, and not random(-amount, amount). The unit roll always draws, so the seeded sequence stays stable as amount crosses zero. The sketch's whole pattern of later rolls stays stable with it.
randomGaussian
randomGaussian() -> Double
randomGaussian(mean: Double, deviation: Double) -> Double
A normally distributed random Double, computed with the Marsaglia polar method. The first form is standard normal, and the second takes a mean and a standard deviation. The scatter reads as more natural than the flat spread of random. About 68 percent of samples land within one deviation of the mean, and about 95 percent within two. See the Gaussian example.
let x = randomGaussian(mean: width / 2, deviation: 80) // clustered near the middle
drawCircle(x, height / 2, 4)
randomVector
randomVector(in rect: Rectangle) -> Vector2
A random point inside rect. Each coordinate is uniform within its own bounds.
let p = randomVector(in: Rectangle(x: 0, y: 0, width: width, height: height))
drawCircle(center: p, radius: 3)
randomVector, in a ring
randomVector(innerRadius: Double, outerRadius: Double) -> Vector2
A random point in the ring between the two radii. The ring is centered on the origin, so add a center to place it elsewhere. See the Ring example.
let p = center + randomVector(innerRadius: 50, outerRadius: 100) // `center` is the canvas middle
drawCircle(center: p, radius: 3)
Choices
randomChoice
randomChoice<T>(_ choices: [T]) -> T
randomChoice<T>(_ choices: [T], weights: [Double]) -> T
A random element of choices. Without weights each element is equally likely, and with them the pick is biased. This is the everyday way to pick from a palette, with no indexing arithmetic to write. Pass one weight per choice. A weight must be non-negative and can be in any scale, so the weights need not sum to 1. A choice weighted 0 is never picked, and choices must not be empty. The picks are seeded like everything random, so randomSeed makes them reproducible.
fill(randomChoice(palette)) // any of them
fill(randomChoice(palette, weights: [6, 3, 1])) // a dominant, a support, a spice
let move = randomChoice(["up", "down", "hold"], weights: [1, 1, 4])
shuffled
shuffled<T>(_ array: [T]) -> [T]
The elements of array in a random order. The order comes from the sketch's seeded generator, so a seeded shuffle reproduces run to run. An array's own shuffled() uses the system's generator instead, so it differs every run.
randomSeed(9)
let order = shuffled(palette) // the same reordering every run
MarkovChain, a choice that remembers
MarkovChain<Element>(order: Int = 1, seed: Int = 0)
mutating func learn(_ element: Element, after context: [Element], times: Int = 1)
mutating func next() -> Element?
A choice that depends on the choices before it. A MarkovChain holds, for each run of recent picks, how often each element came next, and next() picks from those counts. order is how many recent picks it looks at. You can show it a sequence to learn from, or declare the counts directly with learn(_:after:times:). A declared table can be counted off a picture or written by hand. This builds a palette where a color rarely follows another color directly, so a black usually sits between them:
var paints = MarkovChain<String>(order: 1, seed: variation)
paints.learn("red", after: ["red"], times: 9) // a color mostly carries on
paints.learn("black", after: ["red"]) // and ends in black
paints.learn("blue", after: ["blue"], times: 9)
paints.learn("black", after: ["blue"])
paints.learn("black", after: ["black"], times: 2) // black hands over to either color
paints.learn("red", after: ["black"])
paints.learn("blue", after: ["black"])
let next = paints.next() ?? "black"
The chain keeps its own generator, seeded by seed, so adding one never moves the sketch's other rolls. It is the same type the music helpers use to pick notes. MarkovChain on the Composition page covers learning from a sequence, falling back to shorter contexts, and reading the counts back. The Hiroshi Kawano recreations walk a grid of colors with a table counted off his 1964 designs.
Seeding
randomSeed
randomSeed(_ seed: Int)
Seed the generator behind random* so a run reproduces. The same seed gives the same sequence. To reseed noise as well, see seed.
randomSeed(42) // same scatter every run
seed
seed(_ seed: Int)
Seed both random and noise from one value. That locks the whole sketch's randomness, so the sketch reproduces exactly. Use it when one seed should fully determine the work. To reseed only one of the two, use randomSeed or noiseSeed.
seed(7) // random and noise both reproducible
seed also sets the sketch's variation, the seed the run grew from. Every sketch starts on one, rolled fresh unless you call seed, and the sketch reads it back as variation. Every export records it in its recipe. You can step through variations from the inspector's Variation card, or render at any seed with --seed N. See Variations.
override func draw() {
drawCaption("Variation \(variation)")
}
The generator itself
randomness
var randomness: SplitMix64
The sketch's own generator, for anything that takes a using: generator of its own. That is the whole standard library's randomness, and passing this is what keeps those calls on the sketch's seed.
randomSeed(3)
let pick = palette.randomElement(using: &randomness)
let order = points.shuffled(using: &randomness)
let roll = Int.random(in: 1 ... 6, using: &randomness)
let coin = Bool.random(using: &randomness)
Written without using:, points.shuffled() and Int.random(in: 1 ... 6) roll the system's dice instead and differ every run. That is the commonest quiet way a seeded sketch stops reproducing, because nothing about the call looks wrong. The others, a Date() read and the order a Set walks among them, are listed in Why a run repeats.
It is one stream, not a second generator beside random(), so a draw through either spelling advances the other. It is also what the seedable generators take, which is how the sketch's variation reaches them:
let tiles = wfc.solve(using: &randomness)
let word = plant.expanded(iterations: 5, using: &randomness)
To seed it, call randomSeed or seed. Those also record the seed an export embeds in its recipe, where assigning a generator here does not, so a sketch seeded that way exports without a way back to the picture.