Public
Edited
Mar 18, 2023
Insert cell
Insert cell
Insert cell
renderer.domElement
Insert cell
viewof color = Inputs.color({ value: "#c17dff" })
Insert cell
count = 2 ** 12
Insert cell
fps(timeSec)
Insert cell
Insert cell
particleSystemChunk = `
${myRandChunk}
${easingChunk}
${zoneChunk}
${transformChunk}

mat4 emitterTransform(float timeSec) {
return translate(vec3(-250.0 + 500.0 * fract(timeSec * 0.2), 10.0, 0.0));
}

// Calculates a transform of a single particle at a given time of its lifespan
// given the myRand is procedurally seeded per particle
// @ particleTimeSec - time in [0, lifeSec)
// @ t - phase in [0, 1),
// @ iteration - an integer iteration of particle instance (each iteration seeds another particle)
mat4 particleTransform(in float particleTimeSec, in float t, in float iteration) {


return identity
// * translate(vec3(t * 30.0, 0.0, 0.0))
* scale(mix(vec3(1.0), vec3(randInRange(0.0, 3.0)), abs(sin(t * PI * 5.0))))
// * translate(randInBoxZone(vec3(0.0, -2.0, -2.0), vec3(0.0, 2.0, 2.0))) // initial position in a (flat) box
* translate(vec3(0.0, randInBallZone().yz) * 2.0) // initial position in a ball
* scale(mix(vec3(0.0), vec3(randInRange(0.05, 0.1)), abs(sin(t * PI * 5.0))))
* identity;
}

// Prepares particle iteration variables, seeds the random, returns particle transform
mat4 particleMain(in float timeSec, in float seed, bool debug) {
// if (debug) { return emitterTransform(timeSec); }
if (gl_InstanceID < 10) {
return identity
* translate(vec3(-250.0 + 500.0 * fract((timeSec + float(gl_InstanceID) + 0.5) * 0.2), 10.0, 0.0))
* scale(vec3(1.0))
* identity;
}
myRandSeed = seed;
float lifeSec = randInRange(5.0, 5.0);
float initialLifeOffsetSec = randInRange(-lifeSec, 0.0);
float iterationWithPhase = (timeSec + initialLifeOffsetSec) / lifeSec;
float iteration = floor(iterationWithPhase);
float t = fract(iterationWithPhase);
float particleTimeSec = t * lifeSec;
float emissionTimeSec = timeSec - particleTimeSec;
float iterationSeed = fract(iteration * 1e-10);
myRandSeed = fract(myRandSeed + iterationSeed); // reseed rand for every iteration
return emitterTransform(emissionTimeSec) * particleTransform(particleTimeSec, t, iteration);
}
`
Insert cell
material = {
const material = new THREE.MeshBasicMaterial({
color: color
});
const x = particleSystemChunk;
material.onBeforeCompile = (shader) => {
material.userData.shader = shader;
shader.uniforms.timeSec = { value: 0 };
shader.vertexShader = resolveIncludes(shader.vertexShader)
.replaceAll("instanceMatrix", "instanceMatrix2")
.replace(
"void main() {",
`
uniform float timeSec;
${x}
void main() {
mat4 instanceMatrix2 = particleMain(
timeSec,
(1.0 + float(gl_InstanceID)) / ${toGlslFloatLiteral(count)},
gl_InstanceID == 0);
`
);
};
invalidation.then(() => material.dispose());
return material;
}
Insert cell
Insert cell
instancedMesh1 = {
const geometry = new THREE.SphereGeometry();
const selectedParticleSystemChunk = particleSystemChunk;
const obj = new THREE.InstancedMesh(geometry, material, count);
obj.position.x = -250;
const obj1 = new THREE.Group();
obj1.add(obj);
obj1.rotation.y = Math.PI * 0.66;
const startTimeSec = performance.now() * 0.001;
scene.add(obj1);
invalidation.then(() => {
scene.remove(obj1);
obj.dispose();
});
return obj;
}
Insert cell
instancedMesh2 = {
const geometry = new THREE.SphereGeometry(1, 4, 4);
const selectedParticleSystemChunk = particleSystemChunk;
const obj = new THREE.InstancedMesh(geometry, material, count);
obj.position.x = -250;
const obj1 = new THREE.Group();
obj1.add(obj);
obj1.rotation.y = -Math.PI * 0.66;
const startTimeSec = performance.now() * 0.001;
scene.add(obj1);
invalidation.then(() => {
scene.remove(obj1);
obj.dispose();
});
return obj;
}
Insert cell
instancedMesh3 = {
const geometry = new THREE.SphereGeometry();
const selectedParticleSystemChunk = particleSystemChunk;
const obj = new THREE.InstancedMesh(geometry, material, count);
obj.position.x = 250;
obj.position.z = 1;
const obj1 = new THREE.Group();
obj1.add(obj);
obj1.rotation.y = -Math.PI;
const startTimeSec = performance.now() * 0.001;
scene.add(obj1);
invalidation.then(() => {
scene.remove(obj1);
obj.dispose();
});
return obj;
}
Insert cell
Insert cell
{
if (material.userData.shader) {
material.userData.shader.uniforms.timeSec.value = timeSec;
}
scene.update(timeSec);
renderer.render(scene, scene.camera);
return "update & render";
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
resolveIncludes = {
// based on https://github.com/mrdoob/three.js/blob/b9bc47ab1978022ab0947a9bce1b1209769b8d91/src/renderers/webgl/WebGLProgram.js#L204
function resolveIncludes(string) {
return string.replace(
/^[ \t]*#include +<([\w\d./]+)>/gm,
(match, include) => resolveIncludes(THREE.ShaderChunk[include])
);
}
return resolveIncludes;
}
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more