Published
Edited
Nov 16, 2021
Insert cell
Insert cell
width = 500
Insert cell
height = 500
Insert cell
p = getRandomPalette()
Insert cell
palette = shuffle(p)
Insert cell
displayPalettes(palette)
Insert cell
bnb({
w: width,
h: height,
numFrames: 0,
fps: 30,
record: true,
// video: true,

preload: (s, g) => {
g.prng = seededRandom(s.random(1000));
// params
g.branchCount = 3;
g.layerCount = 1;
g.noiseScale = 100;
g.bordersCount = 2;

// variables params
g.fieldIntensity = g.prng(1, 4);
g.colorSeed = s.int(g.prng(99));
g.noiseSeed = g.prng(0.4, 1);
},

setup: (s, g) => {
// vars
g.bg = s.color(220);
g.branches = [];
g.backgroundLayersColors = [];
g.field = new PerlinNoiseField(s, g.fieldIntensity, g.noiseScale);

g.backgroundLayersCount = s.int(g.prng(60, 100));
g.backgroundLayersColors = array(g.backgroundLayersCount).map(d => palette[randInt(1, palette.length)])

g.borderColors = array(g.bordersCount).map(d => randInt(1, palette.length))

for (let l = 0; l < g.layerCount; l++) {
for (let i = 0; i < g.branchCount; i++) {
const a = new Branch(s, g);
a.field = g.field;
a.branchesTotalCount = g.branchCount;
a.branchIndex = i;
a.resetPosition();

const rand1 = randInt(1, palette.length);
let rand2 = randInt(1, palette.length);
while (rand2 == rand1) rand2 = randInt(1, palette.length);
const rand3 = randInt(1, palette.length);
const rand4 = randInt(1, palette.length);
a.branchColor = s.color(palette[rand3]);
a.pistilColor = s.color(palette[rand4]);

a.resetPosition();
for (let j = 0; j < a.petalCount; j++) {
const p = a.petals[j];
p.petalColorBase = s.color(palette[rand1]);
p.petalColorTop = s.color(palette[rand2]);
}
g.branches.push(a);
}
}
},
draw: (s, t, g) => {
s.fill(g.bg);
s.noStroke();
s.rect(0, 0, width, height);

// background layers
for (let i = 0; i < g.backgroundLayersCount; i++) {
let y = s.map(i, 0, g.backgroundLayersCount, 0, height);
let iRatio = s.map(i, 0, g.backgroundLayersCount, 0.0, 1.0);
let c = s.lerpColor(g.bg, s.color(palette[0]), iRatio);
c = s.lerpColor(c, s.color(0), iRatio);
s.fill(c);
s.beginShape();
s.vertex(0, height);
for (let x = 0; x <= width; x +=2) {
let yMultiplier = (1 - s.abs(s.map(x, 0, width, -1, 1))) * 2;
s.vertex(x, y - 100 + s.pow( (s.noise(x * 0.01 * g.noiseSeed, y) * 100 * yMultiplier * yMultiplier), 1.3));
}
s.vertex(width, height);
s.endShape(s.CLOSE);
}

// flowers
for (let l = 0; l < g.layerCount; l++) {
let layerStartIndex = l * g.branchCount;
for (let i = 0; i < g.branchCount; i++) {
const a = g.branches[layerStartIndex + i];
a.draw(s);
}
}

// borders
const borderSize = width * (30/540);
for (let i = g.bordersCount; i > 0; i--) {
const ratio = s.map(i, 0, g.bordersCount, 0, 1);
const c = s.lerpColor(s.color(200), s.color(230), ratio);
const size = borderSize * ratio;

if (i % (g.bordersCount/2) == 0) {
s.strokeWeight(1);
s.stroke(s.color(0));
}

s.fill(c);
s.noStroke();
s.push();
const translateX = width/2;
const translateY = width/2;
s.translate(translateX, translateY);
//s.rotate(s.PI/2 * ratio);
s.rect(-translateX, -translateY, width, size);
s.rect(-translateX, height-size-translateY, width, size);
s.rect(-translateX, -translateY, size, height);
s.rect(width-size-translateX, -translateY, size, height);
s.pop();
}

const N = 10;
for (let j = 0; j<=N;j++) {
const jRatio = s.map(j, 0, N, 0, 1);
const triangleHeight = height/N;
const yMid = height * jRatio;
const y1 = yMid - triangleHeight/2;
const y2 = yMid + triangleHeight/2;
const triangleColor = s.color(palette[0]);
s.noFill();
s.stroke(triangleColor);
s.triangle(0, y1, borderSize/2, yMid, 0, y2);
s.triangle(borderSize/2, y1, 0, yMid, borderSize/2, y2);
s.triangle(width-borderSize/2, y1, width, yMid, width-borderSize/2, y2);
s.triangle(width, y1, width-borderSize/2, yMid, width, y2);

const xMid = width * jRatio;
const x1 = yMid - triangleHeight/2;
const x2 = yMid + triangleHeight/2;
s.triangle(x1, 0, xMid, borderSize/2, x2, 0);
s.triangle(x1, borderSize/2, xMid, 0, x2, borderSize/2);
s.triangle(x1, height-borderSize/2, xMid, height, x2, height-borderSize/2);
s.triangle(x1, height, xMid, height-borderSize/2, x2, height);
//s.line(0, yMid, borderSize/2, yMid);
}
}
})
Insert cell
class Branch {
constructor(s, g) {
this.s = s;
this.g = g;
this.branchSize = g.prng(1, 4);
this.stepSize = g.prng(-1.5, -1.2);
this.pistilRadius = this.branchSize * 2;
this.pistilColor = s.color(255, 255, 0);
this.petalCount = s.int(g.prng(5, 12));
this.petalSize = g.prng(this.branchSize*5.5, 25);

this.branchColor = s.color(255, 0, 0);
this.branchIndex = 1;
this.branchesTotalCount = 10;
this.opacity = 0.1;
this.angle = g.prng(2 * s.PI);
this.directionAngle = 0;
this.isPositionResetWhenOutside = false;
this.life = 0;

this.positions = [];
this.startPosition = new PVector();
this.position = new PVector();
this.previousPosition = new PVector();
this.positionsWithOffset = [];
this.petals = [];

for (let i = 0; i < this.petalCount; i++) {
const a = new Petal(s, g);
a.petalIndex = i;
a.petalSize = this.petalSize;
a.rotationAngle = s.map(i, 0, this.petalCount-1, -s.PI/2, s.PI/2);
let radius = 3;
let x = 0;//radius * cos(2*PI/petalCount * i);
let y = 0;//radius * sin(2*PI/petalCount * i);
a.positionOffset = PVector(x, y);
a.branchAngle = this.angle;
this.petals.push(a);
}
}
getPositions() {
let pos = new PVector();
pos = this.startPosition.copy();

while (this.life < 1) {
this.life += this.lifeSpeed;
let angl = this.field.getNoiseValue(pos);
let movingSpeed = this.stepSize * (1 - this.life);
pos.x += (0.2 * this.s.cos(angl)) * movingSpeed;
pos.y = this.s.max(pos.y + this.s.sin(angl) * movingSpeed, 100);
this.positions.push(pos.copy());
this.positionsWithOffset.push(pos.copy());
}

this.previousPosition = this.positions[this.positions.length - 2].copy();
this.position = this.positions[this.positions.length - 1].copy()
this.directionAngle = this.s.atan2(this.previousPosition.y - this.position.y, this.previousPosition.x - this.position.x) - this.s.PI/2;

for (let i = 0; i < this.petalCount; i++) {
const p = this.petals[i];
p.updatePosition(this.position);
p.updateAngle(this.directionAngle);
}
}
draw(s) {
if ((this.isPositionResetWhenOutside && this.isOutsideSketch() > 0)) {
return;
}
s.stroke(this.branchColor);
s.fill(this.branchColor);

s.beginShape(s.POINTS);

for (let i = 0; i < this.positions.length; i++) {
let p = this.positions[i];
let pp = this.positionsWithOffset[i];

let yRatio = s.map(p.y, this.startPosition.y, this.position.y, 0, 1);
let branchLengthRatio = 1 - this.position.y / height;
s.strokeWeight(s.max(1, (1-yRatio) * this.branchSize + 1));

pp.x = p.x + yRatio * branchLengthRatio;// * noise(p.x/width, sin(time), cos(time)) * 200;
s.vertex(pp.x, p.y);
}

s.endShape();

this.previousPosition = this.positionsWithOffset[this.positionsWithOffset.length - 2].copy();
this.position = this.positionsWithOffset[this.positionsWithOffset.length - 1].copy();
this.directionAngle = s.atan2(this.previousPosition.y - this.position.y, this.previousPosition.x - this.position.x) - s.PI/2;

//ellipse(position.x, position.y, branchSize, branchSize);

this.drawPetals(s);
}
drawPetals(s) {
for (let i = 0; i < this.petalCount; i++) {
const p = this.petals[i];
p.updatePosition(this.position);
p.draw(s);
}

this.s.fill(this.pistilColor);
this.s.ellipse(this.position.x, this.position.y, this.pistilRadius, this.pistilRadius);
}

resetPosition() {
let indexRatio = (this.s.int(this.branchIndex + 1) / this.s.int(this.branchesTotalCount));
let direction = this.branchIndex % 2 == 0 ? 1 : -1;
let x = width/2 + indexRatio * this.g.prng(1, 100) * direction;
this.position = new PVector(x, height-0.1);
this.angle = 0;
this.previousPosition = this.position.copy();
this.startPosition = this.position.copy();
this.lifeSpeed = this.s.map(
this.s.float(this.branchIndex),
this.s.float(this.branchesTotalCount),
0.0,
this.g.prng(0.0045, 0.01),
0.00105
);

this.petalSize = this.s.map(
this.s.float(this.branchIndex),
this.s.float(this.branchesTotalCount),
0.0,
5,
30
);

for (let i = 0; i < this.petalCount; i++) {
const p = this.petals[i];
p.petalSize = this.petalSize;
}
this.getPositions();
}
isOutsideSketch() {
if (this.position.y < 0) {
return 1;
} else if (this.position.x > width) {
return 2;
} else if (this.position.y > height) {
return 3;
} else if (this.position.x < 0) {
return 4;
} else {
return 0;
}
}
}
Insert cell
class Petal {
constructor(s, g) {
this.s = s;
this.g = g;

this.position = new PVector(g.prng(width), g.prng(height));
this.positionOffset = new PVector(g.prng(-10, 10), g.prng(-10, 10));
this.petalColorBase = s.color(255, 0, 0);
this.petalColorTop = s.color(0, 0, 255);
this.rotationAngle = g.prng(2*s.PI);
this.petalIndex = 1;
this.petalSize = g.prng(5);
this.opacity = 1;
this.branchAngle = 0;
this.petalAngleOffset = -(g.prng(s.PI/5)-g.prng(s.PI/5));
}
draw(s) {
s.noStroke();
s.fill(this.petalColorBase);

s.push();
s.translate(this.position.x, this.position.y);
s.rotate(this.branchAngle - this.rotationAngle);
s.translate(0, -this.petalSize);

const drawCount = 10.0;
for (let i = drawCount; i > 0; i--) {
let ratio = i / drawCount;
const c = s.lerpColor(this.petalColorTop, this.petalColorBase, ratio);
s.fill(c);
s.ellipse(0, 0, this.petalSize * ratio, this.petalSize*2 * ratio);
}


s.translate(-this.position.x, -this.position.y);
s.pop();
}

updateAngle(directionAngle) {
this.branchAngle = directionAngle + this.petalAngleOffset + this.s.PI;
}

updatePosition(pos) {
this.position = pos.copy().add(this.positionOffset);
}
}
Insert cell
class PerlinNoiseField {
constructor(s, fieldIntensity, noiseScale) {
this.fieldIntensity = fieldIntensity;
this.noiseScale = noiseScale;
this.s = s;
}
getNoiseValue(position) {
return this.s.noise(position.x / this.noiseScale, position.y / this.noiseScale) * this.fieldIntensity;
}
}
Insert cell
Insert cell
prnga = seededRandom(200)
Insert cell
import {bnb} from '@makio135/bnb'
Insert cell
import {random, randInt, array, shuffle, linearstep, linearstepUpDown, seededRandom } from '@makio135/utilities'
Insert cell
import {getRandomPalette, displayPalettes, scales, ramp} from '@makio135/give-me-colors'
Insert cell
easing = require('d3-ease')
Insert cell
PVector = require('pvectorjs/index.js')
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more