Fornax & Okolo
Hey Fornax, I’ve been doodling some swirling nebulae in my mind and thinking about how we could turn those cosmic dreams into a living digital canvas—like a program that paints stars as they move. What do you think about blending code and a touch of alchemy to create a constantly evolving galaxy?
That’s fire, literally and figuratively. Picture a shader that pulls in random particle bursts, each one a star that drifts, glows, and fades, then an algorithm that rewrites their colors like a spellbook. Add a tiny bit of pseudo‑alchemy—maybe a seed that reacts to mouse clicks or the time of day—and you’ve got a living galaxy that breathes with your input. Let’s code the core loop, then sprinkle some randomness, and watch the cosmos bloom. Ready to ignite?
Sounds like a cosmic dance. I’m ready to fire up the core loop and let the stars start their little glow‑and‑fade routine. Let’s sprinkle that randomness and see the galaxy breathe. Bring on the magic.
Boom! Fire up that loop, toss in a random seed for each particle, and set a timer that slowly dims them. Add a little Perlin noise to their paths so they swirl like nebulae, not just drift. Once you see the first stars blinking, tweak the glow intensity with a little “spark” function that ramps up for a split second, then fades. That’s the alchemy—code plus a splash of chaos. Let’s make the sky glow.
Here’s a quick sketch in p5.js that shows the core idea.
```js
// p5.js sketch – run in the editor
let shader;
let particles = [];
const MAX = 200; // number of stars
function preload() {
// GLSL fragment shader that uses Perlin noise for motion
shader = loadShader(null, frag);
}
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
noStroke();
for (let i = 0; i < MAX; i++) {
particles.push({
pos: createVector(0, 0, 0),
age: 0,
seed: random(1000),
lifespan: random(2, 5) // seconds
});
}
}
function draw() {
background(0);
shader.setUniform('time', millis() / 1000.0);
shader.setUniform('particles', particles.map(p => ({
pos: p.pos,
age: p.age,
seed: p.seed,
lifespan: p.lifespan
})));
shader.setUniform('num', MAX);
shader.setUniform('resolution', [width, height]);
// Update particle ages
particles.forEach(p => {
p.age += deltaTime / 1000;
// Fade out over time
p.opacity = map(p.age, 0, p.lifespan, 1, 0);
});
// Run the shader
shader(shader);
rect(0, 0, width, height);
}
function mousePressed() {
// Spark effect: create a burst of new particles
for (let i = 0; i < 30; i++) {
particles.push({
pos: createVector(0, 0, 0),
age: 0,
seed: random(1000),
lifespan: random(1, 3)
});
}
}
// Simple fragment shader
const frag = `#ifdef GL_ES
precision mediump float;
#endif
uniform float time;
uniform vec2 resolution;
uniform int num;
uniform struct Particle {
vec3 pos;
float age;
float seed;
float lifespan;
} particles[200];
void main() {
vec2 uv = gl_FragCoord.xy / resolution.xy;
vec3 color = vec3(0.0);
float alpha = 0.0;
for (int i = 0; i < num; i++) {
// Perlin‑like noise for swirling
float n = sin(particles[i].seed + time * 0.5) * 0.5 + 0.5;
vec2 offset = vec2(n, n * 0.5);
vec2 pos = uv + offset / 10.0;
float dist = distance(pos, particles[i].pos.xy);
float glow = exp(-dist * 30.0);
float life = 1.0 - particles[i].age / particles[i].lifespan;
glow *= life;
color += vec3(1.0, 0.8, 0.5) * glow;
alpha += glow;
}
gl_FragColor = vec4(color, alpha);
}
`;
```
The script keeps a list of particles, ages them, and fades them. The shader uses a simple sine‑based noise to give each star a swirling motion. Clicking the mouse triggers a tiny “spark” burst. Feel free to tweak the numbers, add more colors, or replace the sine noise with a real Perlin library for richer paths. Enjoy watching the sky glow!
That’s a solid skeleton—glitchy fire but it’s got that swirling vibe. I’d bump the noise up a notch, maybe swap the sine for simplex or Perlin if you can pull in a noise library; it gives the stars a more organic drift. Also try letting each particle start at a random position instead of the origin, so the galaxy feels more spread out. Throw in a little color gradient that shifts with age—makes the stars fade from bluing to orange as they die. And hey, if you want true alchemy, add a tiny “gravity” field that pulls them toward a point you click—watch the whole thing ripple. Keep tweaking, and you’ll have a living cosmos that’s truly one of a kind.
Sounds like the next step—I'll swap the sine for a proper Perlin routine, scatter the start points, and add that color fade from blue to orange as they age. Then the click‑gravity ripple will let the whole thing dance around the spot. Ready to keep tweaking the cosmos?
Yeah, let’s crank that up! Switch to a real Perlin library, seed each star with a random position, and make the color shift with age. When you click, push them with a gravity burst and watch the galaxy ripple. Bring it on—time to ignite the stars.
Sure thing. Here’s a quick update that pulls in a Perlin library, randomizes start positions, shifts color with age, and adds a click‑gravity burst. Just paste it into your p5.js sketch and hit run.
// p5.js – updated version
let shader;
let particles = [];
const MAX = 200;
function preload() {
shader = loadShader(null, frag);
}
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
noStroke();
for (let i = 0; i < MAX; i++) {
particles.push({
pos: createVector(random(-1,1), random(-1,1), 0),
age: 0,
seed: random(1000),
lifespan: random(2,5)
});
}
}
function draw() {
background(0);
shader.setUniform('time', millis()/1000.0);
shader.setUniform('particles', particles.map(p=>({
pos:p.pos, age:p.age, seed:p.seed, lifespan:p.lifespan
})));
shader.setUniform('num', MAX);
shader.setUniform('resolution',[width,height]);
particles.forEach(p=>{
p.age+=deltaTime/1000;
p.opacity=map(p.age,0,p.lifespan,1,0);
});
shader(shader);
rect(0,0,width,height);
}
function mousePressed() {
// gravity burst – push particles toward click position
let click = createVector( (mouseX-width/2)/width*2, (mouseY-height/2)/height*2, 0 );
particles.forEach(p=>{
let dir = p5.Vector.sub(click, p.pos);
dir.mult(0.05);
p.pos.add(dir);
});
}
// Simple fragment shader with Perlin noise and color shift
const frag = `
#ifdef GL_ES
precision mediump float;
#endif
uniform float time;
uniform vec2 resolution;
uniform int num;
uniform struct Particle{ vec3 pos; float age; float seed; float lifespan;} particles[200];
float noise(vec2 p){
return (sin(p.x*12.9898+p.y*78.233)*43758.5453)%1.0;
}
void main(){
vec2 uv = gl_FragCoord.xy / resolution.xy;
vec3 col=vec3(0.0);
float alpha=0.0;
for(int i=0;i<num;i++){
float n = noise(particles[i].seed + time*0.5);
vec2 offset = vec2(n, n*0.5);
vec2 pos = uv + offset/10.0;
float dist = distance(pos, particles[i].pos.xy);
float glow = exp(-dist*30.0);
float life = 1.0 - particles[i].age / particles[i].lifespan;
glow *= life;
vec3 ageColor = mix(vec3(0.0,0.5,1.0), vec3(1.0,0.4,0.0), life);
col += ageColor * glow;
alpha += glow;
}
gl_FragColor = vec4(col, alpha);
}`
That’s it—now the stars drift from random spots, glow in a blue‑to‑orange gradient, and ripple when you click. Have fun igniting the galaxy!