xxxxxxxxxx
223
// jshint esversion: 9
/*
This script can render avi's and gifs. Setup recording in setup().
Press enter to start and stop recording.
Will also stop recording after recording 'FRAMES' frames. Use this for perfect loops :)
libraries used:
Video Builder: https://github.com/theshock/VideoBuilder
download: https://github.com/rndme/download
*/
const FPS = 25;
const RECORDING = false;
const WIDTH = 1080;
const HEIGHT = 1920;
const START_VEL = 0.002;
const LIFE_TIME = 25;
const MAX_PARTICLES = 2 ** 10;
const SPLIT_COUNT = 2;
const SPLIT_ANGLE = 0; //0.5 / 4;
const SPLIT_DIR_ANGLE = 1 / 3 / 4 / LIFE_TIME; //0.0025;
const FADE = 0.05;
const EXPENSIVE_FADE_CORRECTION = true;
const DEFAULT_PARTICLE = {
x: 0.5, y: 2 / 3, r: 0.01,
dir: -0.25, vel: START_VEL, dir_vel: 0,
life_time: LIFE_TIME, frame: 0,
depth: 0,
dead: false,
}
let size, offset, rec;
function setup() {
frameRate(FPS);
const canvas = createCanvas(WIDTH, HEIGHT);
size = min(width, height);
if (width > height)
offset = { x: (width - height) / 2, y: 0 };
else
offset = { x: 0, y: (height - width) / 2 };
rec = setupRecording("particles - genuary #1", canvas, FRAMES, {
avi: {
fps: 60,
quality: 1
},
});
background(0);
addParticle();
}
let t;
function draw() {
t = fract(frameCount / FRAMES);
drawScene();
if (rec.recording)
rec.recordFrame();
}
let ids = 0;
let particles = [];
function addParticle(particle = {}) {
const id = ids++;
particle = { DEFAULT_PARTICLE, particle, id }
if (id % 2 == 0)
particles.push(particle);
else
particles.unshift(particle);
}
function updateParticle(particle) {
let {
x, y, r, dir, vel, dir_vel,
frame, life_time,
dead,
} = particle;
const life_f = frame / life_time;
x += cosn(dir) * vel;
y += sinn(dir) * vel;
dir += dir_vel;
if ((x - r) * size + offset.x > width
|| (x + r) * size + offset.x < 0
|| (y - r) * size + offset.y > height
|| (y + r) * size + offset.y < 0
)
dead = true;
if (life_f >= 1) {
particles = particles.filter(p => p != particle);
splitParticle(particle);
}
frame++;
particle.x = x;
particle.y = y;
particle.dir = dir;
particle.frame = frame;
particle.dead = dead;
}
function splitParticle(particle) {
const {
x, y, dir, vel,
life_time,
depth,
dead,
} = particle;
if (particles.length >= MAX_PARTICLES) return;
const new_depth = depth + 1;
const count = SPLIT_COUNT;
for (let i=0; i<count; i++) {
ff = i / (count - 1);
const new_life_time = life_time * 1;
const dir_angle = SPLIT_DIR_ANGLE;
const dir_vel = lerp(-dir_angle, dir_angle, ff);
const angle = SPLIT_ANGLE;
const new_dir = dir + lerp(-angle, angle, ff) //- dir_vel * new_life_time;
addParticle({
x, y, vel, dir_vel,
dir: new_dir,
depth: new_depth,
life_time: new_life_time,
dead,
});
}
}
function drawParticle(particle) {
const { x, y, r,
life_time, frame,
depth, id,
dead,
} = particle;
if (dead) return;
const life_f = frame / life_time;
const life_color_range = 0.1;
const color_f = depth * life_color_range + life_f * life_color_range;
const color1 = [1, 0, 0.5];
const color2 = [1, 0.5, 0];
const color = color1.map((c, i) => lerp(c, color2[i], sinn(color_f + (x + y) / 1.5)) * 255);
const draw_particle = {
x, y, r, color,
frame: 0,
}
fill(color);
noStroke();
circle(x * size, y * size, r * size);
}
function fadeOut() {
loadPixels();
for (let i=0; i<pixels.length; i++) {
if (pixels[i] < 10)
pixels[i] -= 1;
}
updatePixels();
}
const FRAMES = 251;
function drawScene() {
if (frameCount === FRAMES) {
particles = [];
addParticle();
const life_time_offset = LIFE_TIME - 10;
for (let i=0; i<life_time_offset; i++)
[particles].forEach(updateParticle)
frameCount = life_time_offset + 1;
}
if (RECORDING && frameCount == floor(FRAMES / 3)) {
if (!rec.recording)
rec.start();
else
rec.stop();
}
[particles].forEach(updateParticle);
translate(offset.x, offset.y);
background(0, 0, 0, 255 * FADE);
if (EXPENSIVE_FADE_CORRECTION) fadeOut();
particles.forEach(drawParticle);
}
const sinn = (v) => sin(v * TAU);
const cosn = (v) => cos(v * TAU);
const ncosn = (v) => cosn(v) * 0.5 + 0.5;